bmad-method 6.0.4 → 6.0.5-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment/code_review_guidelines.yaml +2 -42
- package/.claude/skills/bmad-os-findings-triage/SKILL.md +6 -0
- package/.claude/skills/bmad-os-findings-triage/prompts/agent-prompt.md +104 -0
- package/.claude/skills/bmad-os-findings-triage/prompts/instructions.md +286 -0
- package/.claude/skills/bmad-os-review-pr/SKILL.md +1 -1
- package/.claude/skills/bmad-os-review-pr/prompts/instructions.md +63 -6
- package/.claude/skills/bmad-os-review-prompt/SKILL.md +177 -0
- package/.github/workflows/publish.yaml +243 -0
- package/CONTRIBUTING.md +1 -1
- package/README_CN.md +121 -0
- package/docs/_STYLE_GUIDE.md +10 -10
- package/docs/explanation/brainstorming.md +1 -1
- package/docs/explanation/party-mode.md +1 -1
- package/docs/explanation/preventing-agent-conflicts.md +1 -1
- package/docs/explanation/project-context.md +15 -15
- package/docs/explanation/quick-flow.md +9 -9
- package/docs/how-to/established-projects.md +7 -7
- package/docs/how-to/get-answers-about-bmad.md +2 -2
- package/docs/how-to/install-bmad.md +16 -6
- package/docs/how-to/project-context.md +2 -2
- package/docs/how-to/quick-fixes.md +5 -5
- package/docs/how-to/shard-large-documents.md +1 -1
- package/docs/how-to/upgrade-to-v6.md +8 -5
- package/docs/index.md +1 -1
- package/docs/reference/agents.md +14 -14
- package/docs/reference/commands.md +64 -70
- package/docs/reference/testing.md +1 -1
- package/docs/reference/workflow-map.md +19 -19
- package/docs/tutorials/getting-started.md +34 -34
- package/docs/zh-cn/404.md +9 -0
- package/docs/zh-cn/_STYLE_GUIDE.md +370 -0
- package/docs/zh-cn/explanation/advanced-elicitation.md +62 -0
- package/docs/zh-cn/explanation/adversarial-review.md +71 -0
- package/docs/zh-cn/explanation/brainstorming.md +43 -0
- package/docs/zh-cn/explanation/established-projects-faq.md +60 -0
- package/docs/zh-cn/explanation/party-mode.md +79 -0
- package/docs/zh-cn/explanation/preventing-agent-conflicts.md +137 -0
- package/docs/zh-cn/explanation/project-context.md +176 -0
- package/docs/zh-cn/explanation/quick-flow.md +93 -0
- package/docs/zh-cn/explanation/why-solutioning-matters.md +90 -0
- package/docs/zh-cn/how-to/customize-bmad.md +182 -0
- package/docs/zh-cn/how-to/established-projects.md +134 -0
- package/docs/zh-cn/how-to/get-answers-about-bmad.md +144 -0
- package/docs/zh-cn/how-to/install-bmad.md +105 -0
- package/docs/zh-cn/how-to/non-interactive-installation.md +181 -0
- package/docs/zh-cn/how-to/project-context.md +152 -0
- package/docs/zh-cn/how-to/quick-fixes.md +140 -0
- package/docs/zh-cn/how-to/shard-large-documents.md +86 -0
- package/docs/zh-cn/how-to/upgrade-to-v6.md +120 -0
- package/docs/zh-cn/index.md +69 -0
- package/docs/zh-cn/reference/agents.md +41 -0
- package/docs/zh-cn/reference/commands.md +166 -0
- package/docs/zh-cn/reference/modules.md +94 -0
- package/docs/zh-cn/reference/testing.md +122 -0
- package/docs/zh-cn/reference/workflow-map.md +104 -0
- package/docs/zh-cn/roadmap.mdx +152 -0
- package/docs/zh-cn/tutorials/getting-started.md +300 -0
- package/package.json +1 -1
- package/src/bmm/agents/analyst.agent.yaml +1 -1
- package/src/bmm/agents/bmad-skill-manifest.yaml +39 -0
- package/src/bmm/agents/dev.agent.yaml +2 -2
- package/src/bmm/agents/pm.agent.yaml +1 -1
- package/src/bmm/agents/qa.agent.yaml +1 -1
- package/src/bmm/agents/quick-flow-solo-dev.agent.yaml +6 -2
- package/src/bmm/agents/sm.agent.yaml +4 -4
- package/src/bmm/agents/tech-writer/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/agents/tech-writer/tech-writer.agent.yaml +1 -1
- package/src/bmm/module-help.csv +11 -10
- package/src/bmm/workflows/1-analysis/create-product-brief/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +1 -1
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +1 -1
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +1 -1
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +1 -1
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +1 -1
- package/src/bmm/workflows/1-analysis/research/bmad-skill-manifest.yaml +14 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/bmad-skill-manifest.yaml +14 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +1 -1
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +2 -2
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +1 -1
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +1 -1
- package/src/bmm/workflows/3-solutioning/create-architecture/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +2 -2
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +2 -2
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +2 -2
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +2 -2
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +2 -2
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +2 -2
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +1 -1
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +1 -1
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +1 -1
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +1 -1
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +2 -2
- package/src/bmm/workflows/4-implementation/code-review/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/4-implementation/code-review/discover-inputs.md +88 -0
- package/src/bmm/workflows/4-implementation/code-review/workflow.md +271 -0
- package/src/bmm/workflows/4-implementation/correct-course/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/4-implementation/correct-course/checklist.md +1 -1
- package/src/bmm/workflows/4-implementation/correct-course/{instructions.md → workflow.md} +79 -12
- package/src/bmm/workflows/4-implementation/create-story/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +9 -10
- package/src/bmm/workflows/4-implementation/create-story/discover-inputs.md +88 -0
- package/src/bmm/workflows/4-implementation/create-story/workflow.md +388 -0
- package/src/bmm/workflows/4-implementation/dev-story/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/4-implementation/dev-story/{instructions.xml → workflow.md} +49 -2
- package/src/bmm/workflows/4-implementation/retrospective/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/4-implementation/retrospective/{instructions.md → workflow.md} +64 -23
- package/src/bmm/workflows/4-implementation/sprint-planning/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +1 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/{instructions.md → workflow.md} +55 -10
- package/src/bmm/workflows/4-implementation/sprint-status/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/4-implementation/sprint-status/{instructions.md → workflow.md} +45 -8
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/SKILL.md +6 -0
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/bmad-skill-manifest.yaml +1 -0
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-01-clarify-and-route.md +54 -0
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-02-plan.md +39 -0
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-03-implement.md +35 -0
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-04-review.md +55 -0
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-05-present.md +19 -0
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/tech-spec-template.md +90 -0
- package/src/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/workflow.md +84 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +8 -14
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +1 -1
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +4 -6
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +1 -1
- package/src/bmm/workflows/document-project/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/document-project/instructions.md +5 -7
- package/src/bmm/workflows/document-project/workflow.md +39 -0
- package/src/bmm/workflows/document-project/workflows/deep-dive-instructions.md +0 -1
- package/src/bmm/workflows/document-project/workflows/deep-dive-workflow.md +42 -0
- package/src/bmm/workflows/document-project/workflows/full-scan-instructions.md +0 -1
- package/src/bmm/workflows/document-project/workflows/full-scan-workflow.md +42 -0
- package/src/bmm/workflows/generate-project-context/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +2 -2
- package/src/bmm/workflows/qa-generate-e2e-tests/bmad-skill-manifest.yaml +3 -0
- package/src/bmm/workflows/qa-generate-e2e-tests/checklist.md +1 -1
- package/src/bmm/workflows/qa-generate-e2e-tests/{instructions.md → workflow.md} +40 -7
- package/src/core/agents/bmad-master.agent.yaml +1 -1
- package/src/core/agents/bmad-skill-manifest.yaml +3 -0
- package/src/core/module-help.csv +3 -3
- package/src/core/module.yaml +1 -1
- package/src/core/tasks/bmad-help/SKILL.md +6 -0
- package/src/core/tasks/bmad-help/bmad-skill-manifest.yaml +1 -0
- package/src/core/tasks/{help.md → bmad-help/workflow.md} +6 -4
- package/src/core/tasks/bmad-review-adversarial-general/SKILL.md +6 -0
- package/src/core/tasks/bmad-review-adversarial-general/bmad-skill-manifest.yaml +1 -0
- package/src/core/tasks/bmad-review-adversarial-general/workflow.md +32 -0
- package/src/core/tasks/bmad-review-edge-case-hunter/SKILL.md +6 -0
- package/src/core/tasks/bmad-review-edge-case-hunter/bmad-skill-manifest.yaml +1 -0
- package/src/core/tasks/bmad-review-edge-case-hunter/workflow.md +62 -0
- package/src/core/tasks/bmad-skill-manifest.yaml +19 -0
- package/src/core/workflows/advanced-elicitation/bmad-skill-manifest.yaml +3 -0
- package/src/core/workflows/advanced-elicitation/workflow.md +138 -0
- package/src/core/workflows/brainstorming/bmad-skill-manifest.yaml +3 -0
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +1 -1
- package/src/core/workflows/brainstorming/workflow.md +1 -1
- package/src/core/workflows/party-mode/bmad-skill-manifest.yaml +3 -0
- package/src/utility/agent-components/activation-steps.txt +2 -2
- package/src/utility/agent-components/handler-multi.txt +1 -2
- package/test/adversarial-review-tests/README.md +3 -3
- package/test/adversarial-review-tests/test-cases.yaml +2 -2
- package/test/fixtures/agent-schema/valid/menu/multiple-menu-items.agent.yaml +1 -1
- package/test/fixtures/agent-schema/valid/menu-commands/all-command-types.agent.yaml +1 -1
- package/test/fixtures/agent-schema/valid/menu-commands/multiple-commands.agent.yaml +1 -2
- package/test/fixtures/file-refs-csv/valid/bmm-style.csv +1 -1
- package/test/test-file-refs-csv.js +1 -1
- package/test/test-install-to-bmad.js +154 -0
- package/test/test-installation-components.js +1586 -2
- package/test/test-workflow-path-regex.js +88 -0
- package/tools/cli/installers/lib/core/installer.js +34 -1
- package/tools/cli/installers/lib/core/manifest-generator.js +328 -35
- package/tools/cli/installers/lib/ide/_base-ide.js +24 -15
- package/tools/cli/installers/lib/ide/_config-driven.js +472 -53
- package/tools/cli/installers/lib/ide/manager.js +23 -61
- package/tools/cli/installers/lib/ide/platform-codes.yaml +108 -30
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +1 -0
- package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +7 -0
- package/tools/cli/installers/lib/ide/shared/path-utils.js +68 -3
- package/tools/cli/installers/lib/ide/shared/skill-manifest.js +90 -0
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +2 -0
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +6 -145
- package/tools/cli/installers/lib/modules/manager.js +9 -132
- package/tools/cli/lib/agent/compiler.js +1 -10
- package/tools/cli/lib/agent-analyzer.js +2 -14
- package/tools/cli/lib/yaml-xml-builder.js +1 -18
- package/tools/docs/native-skills-migration-checklist.md +281 -0
- package/tools/platform-codes.yaml +1 -1
- package/tools/schema/agent.js +1 -3
- package/tools/validate-file-refs.js +2 -0
- package/website/astro.config.mjs +24 -3
- package/website/src/content/config.ts +2 -1
- package/website/src/content/i18n/zh-CN.json +28 -0
- package/src/bmm/workflows/4-implementation/code-review/instructions.xml +0 -227
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +0 -43
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +0 -53
- package/src/bmm/workflows/4-implementation/create-story/instructions.xml +0 -346
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +0 -52
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +0 -20
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +0 -52
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +0 -47
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +0 -25
- package/src/bmm/workflows/document-project/workflow.yaml +0 -22
- package/src/bmm/workflows/document-project/workflows/deep-dive.yaml +0 -31
- package/src/bmm/workflows/document-project/workflows/full-scan.yaml +0 -31
- package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +0 -42
- package/src/core/tasks/review-adversarial-general.xml +0 -49
- package/src/core/tasks/review-edge-case-hunter.xml +0 -63
- package/src/core/tasks/workflow.xml +0 -235
- package/src/core/workflows/advanced-elicitation/workflow.xml +0 -118
- package/src/utility/agent-components/handler-validate-workflow.txt +0 -7
- package/src/utility/agent-components/handler-workflow.txt +0 -10
- package/tools/cli/installers/lib/ide/codex.js +0 -440
- package/tools/cli/installers/lib/ide/github-copilot.js +0 -699
- package/tools/cli/installers/lib/ide/kilo.js +0 -269
- package/tools/cli/installers/lib/ide/rovodev.js +0 -257
- package/tools/cli/installers/lib/ide/templates/combined/default-workflow-yaml.md +0 -14
- package/tools/cli/installers/lib/ide/templates/combined/kiro-workflow-yaml.md +0 -15
- package/tools/cli/installers/lib/ide/templates/workflow-command-template.md +0 -13
- package/tools/cli/installers/lib/ide/templates/workflow-commander.md +0 -5
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
const os = require('node:os');
|
|
1
2
|
const path = require('node:path');
|
|
2
3
|
const fs = require('fs-extra');
|
|
4
|
+
const yaml = require('yaml');
|
|
3
5
|
const { BaseIdeSetup } = require('./_base-ide');
|
|
4
6
|
const prompts = require('../../../lib/prompts');
|
|
5
7
|
const { AgentCommandGenerator } = require('./shared/agent-command-generator');
|
|
6
8
|
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
|
|
7
9
|
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
|
|
10
|
+
const csv = require('csv-parse/sync');
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* Config-driven IDE setup handler
|
|
@@ -24,6 +27,34 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
24
27
|
super(platformCode, platformConfig.name, platformConfig.preferred);
|
|
25
28
|
this.platformConfig = platformConfig;
|
|
26
29
|
this.installerConfig = platformConfig.installer || null;
|
|
30
|
+
|
|
31
|
+
// Set configDir from target_dir so base-class detect() works
|
|
32
|
+
if (this.installerConfig?.target_dir) {
|
|
33
|
+
this.configDir = this.installerConfig.target_dir;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detect whether this IDE already has configuration in the project.
|
|
39
|
+
* For skill_format platforms, checks for bmad-prefixed entries in target_dir
|
|
40
|
+
* (matching old codex.js behavior) instead of just checking directory existence.
|
|
41
|
+
* @param {string} projectDir - Project directory
|
|
42
|
+
* @returns {Promise<boolean>}
|
|
43
|
+
*/
|
|
44
|
+
async detect(projectDir) {
|
|
45
|
+
if (this.installerConfig?.skill_format && this.configDir) {
|
|
46
|
+
const dir = path.join(projectDir || process.cwd(), this.configDir);
|
|
47
|
+
if (await fs.pathExists(dir)) {
|
|
48
|
+
try {
|
|
49
|
+
const entries = await fs.readdir(dir);
|
|
50
|
+
return entries.some((e) => typeof e === 'string' && e.startsWith('bmad'));
|
|
51
|
+
} catch {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return super.detect(projectDir);
|
|
27
58
|
}
|
|
28
59
|
|
|
29
60
|
/**
|
|
@@ -39,8 +70,8 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
39
70
|
const conflict = await this.findAncestorConflict(projectDir);
|
|
40
71
|
if (conflict) {
|
|
41
72
|
await prompts.log.error(
|
|
42
|
-
`Found existing BMAD
|
|
43
|
-
` ${this.name} inherits
|
|
73
|
+
`Found existing BMAD skills in ancestor installation: ${conflict}\n` +
|
|
74
|
+
` ${this.name} inherits skills from parent directories, so this would cause duplicates.\n` +
|
|
44
75
|
` Please remove the BMAD files from that directory first:\n` +
|
|
45
76
|
` rm -rf "${conflict}"/bmad*`,
|
|
46
77
|
);
|
|
@@ -86,39 +117,48 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
86
117
|
async installToTarget(projectDir, bmadDir, config, options) {
|
|
87
118
|
const { target_dir, template_type, artifact_types } = config;
|
|
88
119
|
|
|
89
|
-
// Skip targets with explicitly empty artifact_types
|
|
120
|
+
// Skip targets with explicitly empty artifact_types and no verbatim skills
|
|
90
121
|
// This prevents creating empty directories when no artifacts will be written
|
|
91
|
-
|
|
92
|
-
|
|
122
|
+
const skipStandardArtifacts = Array.isArray(artifact_types) && artifact_types.length === 0;
|
|
123
|
+
if (skipStandardArtifacts && !config.skill_format) {
|
|
124
|
+
return { success: true, results: { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 } };
|
|
93
125
|
}
|
|
94
126
|
|
|
95
127
|
const targetPath = path.join(projectDir, target_dir);
|
|
96
128
|
await this.ensureDir(targetPath);
|
|
97
129
|
|
|
98
130
|
const selectedModules = options.selectedModules || [];
|
|
99
|
-
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0 };
|
|
131
|
+
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
|
|
132
|
+
|
|
133
|
+
// Install standard artifacts (agents, workflows, tasks, tools)
|
|
134
|
+
if (!skipStandardArtifacts) {
|
|
135
|
+
// Install agents
|
|
136
|
+
if (!artifact_types || artifact_types.includes('agents')) {
|
|
137
|
+
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
|
138
|
+
const { artifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules);
|
|
139
|
+
results.agents = await this.writeAgentArtifacts(targetPath, artifacts, template_type, config);
|
|
140
|
+
}
|
|
100
141
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
142
|
+
// Install workflows
|
|
143
|
+
if (!artifact_types || artifact_types.includes('workflows')) {
|
|
144
|
+
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
|
145
|
+
const { artifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
|
|
146
|
+
results.workflows = await this.writeWorkflowArtifacts(targetPath, artifacts, template_type, config);
|
|
147
|
+
}
|
|
107
148
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
149
|
+
// Install tasks and tools using template system (supports TOML for Gemini, MD for others)
|
|
150
|
+
if (!artifact_types || artifact_types.includes('tasks') || artifact_types.includes('tools')) {
|
|
151
|
+
const taskToolGen = new TaskToolCommandGenerator(this.bmadFolderName);
|
|
152
|
+
const { artifacts } = await taskToolGen.collectTaskToolArtifacts(bmadDir);
|
|
153
|
+
const taskToolResult = await this.writeTaskToolArtifacts(targetPath, artifacts, template_type, config);
|
|
154
|
+
results.tasks = taskToolResult.tasks || 0;
|
|
155
|
+
results.tools = taskToolResult.tools || 0;
|
|
156
|
+
}
|
|
113
157
|
}
|
|
114
158
|
|
|
115
|
-
// Install
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
const { artifacts } = await taskToolGen.collectTaskToolArtifacts(bmadDir);
|
|
119
|
-
const taskToolResult = await this.writeTaskToolArtifacts(targetPath, artifacts, template_type, config);
|
|
120
|
-
results.tasks = taskToolResult.tasks || 0;
|
|
121
|
-
results.tools = taskToolResult.tools || 0;
|
|
159
|
+
// Install verbatim skills (type: skill)
|
|
160
|
+
if (config.skill_format) {
|
|
161
|
+
results.skills = await this.installVerbatimSkills(projectDir, bmadDir, targetPath, config);
|
|
122
162
|
}
|
|
123
163
|
|
|
124
164
|
await this.printSummary(results, target_dir, options);
|
|
@@ -134,7 +174,7 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
134
174
|
* @returns {Promise<Object>} Installation result
|
|
135
175
|
*/
|
|
136
176
|
async installToMultipleTargets(projectDir, bmadDir, targets, options) {
|
|
137
|
-
const allResults = { agents: 0, workflows: 0, tasks: 0, tools: 0 };
|
|
177
|
+
const allResults = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
|
|
138
178
|
|
|
139
179
|
for (const target of targets) {
|
|
140
180
|
const result = await this.installToTarget(projectDir, bmadDir, target, options);
|
|
@@ -143,6 +183,7 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
143
183
|
allResults.workflows += result.results.workflows || 0;
|
|
144
184
|
allResults.tasks += result.results.tasks || 0;
|
|
145
185
|
allResults.tools += result.results.tools || 0;
|
|
186
|
+
allResults.skills += result.results.skills || 0;
|
|
146
187
|
}
|
|
147
188
|
}
|
|
148
189
|
|
|
@@ -165,8 +206,13 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
165
206
|
for (const artifact of artifacts) {
|
|
166
207
|
const content = this.renderTemplate(template, artifact);
|
|
167
208
|
const filename = this.generateFilename(artifact, 'agent', extension);
|
|
168
|
-
|
|
169
|
-
|
|
209
|
+
|
|
210
|
+
if (config.skill_format) {
|
|
211
|
+
await this.writeSkillFile(targetPath, artifact, content);
|
|
212
|
+
} else {
|
|
213
|
+
const filePath = path.join(targetPath, filename);
|
|
214
|
+
await this.writeFile(filePath, content);
|
|
215
|
+
}
|
|
170
216
|
count++;
|
|
171
217
|
}
|
|
172
218
|
|
|
@@ -186,20 +232,17 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
186
232
|
|
|
187
233
|
for (const artifact of artifacts) {
|
|
188
234
|
if (artifact.type === 'workflow-command') {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const workflowTemplateType = artifact.isYamlWorkflow
|
|
192
|
-
? config.yaml_workflow_template || `${templateType}-workflow-yaml`
|
|
193
|
-
: config.md_workflow_template || `${templateType}-workflow`;
|
|
194
|
-
|
|
195
|
-
// Fall back to default templates if specific ones don't exist
|
|
196
|
-
const finalTemplateType = artifact.isYamlWorkflow ? 'default-workflow-yaml' : 'default-workflow';
|
|
197
|
-
// workflowTemplateType already contains full name (e.g., 'gemini-workflow-yaml'), so pass empty artifactType
|
|
198
|
-
const { content: template, extension } = await this.loadTemplate(workflowTemplateType, '', config, finalTemplateType);
|
|
235
|
+
const workflowTemplateType = config.md_workflow_template || `${templateType}-workflow`;
|
|
236
|
+
const { content: template, extension } = await this.loadTemplate(workflowTemplateType, '', config, 'default-workflow');
|
|
199
237
|
const content = this.renderTemplate(template, artifact);
|
|
200
238
|
const filename = this.generateFilename(artifact, 'workflow', extension);
|
|
201
|
-
|
|
202
|
-
|
|
239
|
+
|
|
240
|
+
if (config.skill_format) {
|
|
241
|
+
await this.writeSkillFile(targetPath, artifact, content);
|
|
242
|
+
} else {
|
|
243
|
+
const filePath = path.join(targetPath, filename);
|
|
244
|
+
await this.writeFile(filePath, content);
|
|
245
|
+
}
|
|
203
246
|
count++;
|
|
204
247
|
}
|
|
205
248
|
}
|
|
@@ -241,8 +284,13 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
241
284
|
|
|
242
285
|
const content = this.renderTemplate(template, artifact);
|
|
243
286
|
const filename = this.generateFilename(artifact, artifact.type, extension);
|
|
244
|
-
|
|
245
|
-
|
|
287
|
+
|
|
288
|
+
if (config.skill_format) {
|
|
289
|
+
await this.writeSkillFile(targetPath, artifact, content);
|
|
290
|
+
} else {
|
|
291
|
+
const filePath = path.join(targetPath, filename);
|
|
292
|
+
await this.writeFile(filePath, content);
|
|
293
|
+
}
|
|
246
294
|
|
|
247
295
|
if (artifact.type === 'task') {
|
|
248
296
|
taskCount++;
|
|
@@ -409,20 +457,144 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
409
457
|
// No default
|
|
410
458
|
}
|
|
411
459
|
|
|
412
|
-
|
|
460
|
+
// Replace _bmad placeholder with actual folder name BEFORE inserting paths,
|
|
461
|
+
// so that paths containing '_bmad' are not corrupted by the blanket replacement.
|
|
462
|
+
let rendered = template.replaceAll('_bmad', this.bmadFolderName);
|
|
463
|
+
|
|
464
|
+
// Replace {{bmadFolderName}} placeholder if present
|
|
465
|
+
rendered = rendered.replaceAll('{{bmadFolderName}}', this.bmadFolderName);
|
|
466
|
+
|
|
467
|
+
rendered = rendered
|
|
413
468
|
.replaceAll('{{name}}', artifact.name || '')
|
|
414
469
|
.replaceAll('{{module}}', artifact.module || 'core')
|
|
415
470
|
.replaceAll('{{path}}', pathToUse)
|
|
416
471
|
.replaceAll('{{description}}', artifact.description || `${artifact.name} ${artifact.type || ''}`)
|
|
417
472
|
.replaceAll('{{workflow_path}}', pathToUse);
|
|
418
473
|
|
|
419
|
-
|
|
420
|
-
|
|
474
|
+
return rendered;
|
|
475
|
+
}
|
|
421
476
|
|
|
422
|
-
|
|
423
|
-
|
|
477
|
+
/**
|
|
478
|
+
* Write artifact as a skill directory with SKILL.md inside.
|
|
479
|
+
* Writes artifact as a skill directory with SKILL.md inside.
|
|
480
|
+
* @param {string} targetPath - Base skills directory
|
|
481
|
+
* @param {Object} artifact - Artifact data
|
|
482
|
+
* @param {string} content - Rendered template content
|
|
483
|
+
*/
|
|
484
|
+
async writeSkillFile(targetPath, artifact, content) {
|
|
485
|
+
const { resolveSkillName } = require('./shared/path-utils');
|
|
424
486
|
|
|
425
|
-
|
|
487
|
+
// Get the skill name (prefers canonicalId, falls back to path-derived) and remove .md
|
|
488
|
+
const flatName = resolveSkillName(artifact);
|
|
489
|
+
const skillName = path.basename(flatName.replace(/\.md$/, ''));
|
|
490
|
+
|
|
491
|
+
if (!skillName) {
|
|
492
|
+
throw new Error(`Cannot derive skill name for artifact: ${artifact.relativePath || JSON.stringify(artifact)}`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Create skill directory
|
|
496
|
+
const skillDir = path.join(targetPath, skillName);
|
|
497
|
+
await this.ensureDir(skillDir);
|
|
498
|
+
|
|
499
|
+
// Transform content: rewrite frontmatter for skills format
|
|
500
|
+
const skillContent = this.transformToSkillFormat(content, skillName);
|
|
501
|
+
|
|
502
|
+
await this.writeFile(path.join(skillDir, 'SKILL.md'), skillContent);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Transform artifact content to Agent Skills format.
|
|
507
|
+
* Rewrites frontmatter to contain only unquoted name and description.
|
|
508
|
+
* @param {string} content - Original content with YAML frontmatter
|
|
509
|
+
* @param {string} skillName - Skill name (must match directory name)
|
|
510
|
+
* @returns {string} Transformed content
|
|
511
|
+
*/
|
|
512
|
+
transformToSkillFormat(content, skillName) {
|
|
513
|
+
// Normalize line endings
|
|
514
|
+
content = content.replaceAll('\r\n', '\n').replaceAll('\r', '\n');
|
|
515
|
+
|
|
516
|
+
// Parse frontmatter
|
|
517
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
518
|
+
if (!fmMatch) {
|
|
519
|
+
// No frontmatter -- wrap with minimal frontmatter
|
|
520
|
+
const fm = yaml.stringify({ name: skillName, description: skillName }).trimEnd();
|
|
521
|
+
return `---\n${fm}\n---\n\n${content}`;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const frontmatter = fmMatch[1];
|
|
525
|
+
const body = fmMatch[2];
|
|
526
|
+
|
|
527
|
+
// Parse frontmatter with yaml library to extract description
|
|
528
|
+
let description;
|
|
529
|
+
try {
|
|
530
|
+
const parsed = yaml.parse(frontmatter);
|
|
531
|
+
const rawDesc = parsed?.description;
|
|
532
|
+
description = typeof rawDesc === 'string' && rawDesc ? rawDesc : `${skillName} skill`;
|
|
533
|
+
} catch {
|
|
534
|
+
description = `${skillName} skill`;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Build new frontmatter with only name and description, unquoted
|
|
538
|
+
const newFrontmatter = yaml.stringify({ name: skillName, description: String(description) }, { lineWidth: 0 }).trimEnd();
|
|
539
|
+
return `---\n${newFrontmatter}\n---\n${body}`;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Install a custom agent launcher.
|
|
544
|
+
* For skill_format platforms, produces <skillDir>/SKILL.md.
|
|
545
|
+
* For flat platforms, produces a single file in target_dir.
|
|
546
|
+
* @param {string} projectDir - Project directory
|
|
547
|
+
* @param {string} agentName - Agent name (e.g., "fred-commit-poet")
|
|
548
|
+
* @param {string} agentPath - Path to compiled agent (relative to project root)
|
|
549
|
+
* @param {Object} metadata - Agent metadata
|
|
550
|
+
* @returns {Object|null} Info about created file/skill
|
|
551
|
+
*/
|
|
552
|
+
async installCustomAgentLauncher(projectDir, agentName, agentPath, metadata) {
|
|
553
|
+
if (!this.installerConfig?.target_dir) return null;
|
|
554
|
+
|
|
555
|
+
const { customAgentDashName } = require('./shared/path-utils');
|
|
556
|
+
const targetPath = path.join(projectDir, this.installerConfig.target_dir);
|
|
557
|
+
await this.ensureDir(targetPath);
|
|
558
|
+
|
|
559
|
+
// Build artifact to reuse existing template rendering.
|
|
560
|
+
// The default-agent template already includes the _bmad/ prefix before {{path}},
|
|
561
|
+
// but agentPath is relative to project root (e.g. "_bmad/custom/agents/fred.md").
|
|
562
|
+
// Strip the bmadFolderName prefix so the template doesn't produce a double path.
|
|
563
|
+
const bmadPrefix = this.bmadFolderName + '/';
|
|
564
|
+
const normalizedPath = agentPath.startsWith(bmadPrefix) ? agentPath.slice(bmadPrefix.length) : agentPath;
|
|
565
|
+
|
|
566
|
+
const artifact = {
|
|
567
|
+
type: 'agent-launcher',
|
|
568
|
+
name: agentName,
|
|
569
|
+
description: metadata?.description || `${agentName} agent`,
|
|
570
|
+
agentPath: normalizedPath,
|
|
571
|
+
relativePath: normalizedPath,
|
|
572
|
+
module: 'custom',
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const { content: template } = await this.loadTemplate(
|
|
576
|
+
this.installerConfig.template_type || 'default',
|
|
577
|
+
'agent',
|
|
578
|
+
this.installerConfig,
|
|
579
|
+
'default-agent',
|
|
580
|
+
);
|
|
581
|
+
const content = this.renderTemplate(template, artifact);
|
|
582
|
+
|
|
583
|
+
if (this.installerConfig.skill_format) {
|
|
584
|
+
const skillName = customAgentDashName(agentName).replace(/\.md$/, '');
|
|
585
|
+
const skillDir = path.join(targetPath, skillName);
|
|
586
|
+
await this.ensureDir(skillDir);
|
|
587
|
+
const skillContent = this.transformToSkillFormat(content, skillName);
|
|
588
|
+
const skillPath = path.join(skillDir, 'SKILL.md');
|
|
589
|
+
await this.writeFile(skillPath, skillContent);
|
|
590
|
+
return { path: path.relative(projectDir, skillPath), command: `$${skillName}` };
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// Flat file output
|
|
594
|
+
const filename = customAgentDashName(agentName);
|
|
595
|
+
const filePath = path.join(targetPath, filename);
|
|
596
|
+
await this.writeFile(filePath, content);
|
|
597
|
+
return { path: path.relative(projectDir, filePath), command: agentName };
|
|
426
598
|
}
|
|
427
599
|
|
|
428
600
|
/**
|
|
@@ -433,10 +605,11 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
433
605
|
* @returns {string} Generated filename
|
|
434
606
|
*/
|
|
435
607
|
generateFilename(artifact, artifactType, extension = '.md') {
|
|
436
|
-
const {
|
|
608
|
+
const { resolveSkillName } = require('./shared/path-utils');
|
|
437
609
|
|
|
438
610
|
// Reuse central logic to ensure consistent naming conventions
|
|
439
|
-
|
|
611
|
+
// Prefers canonicalId from manifest when available, falls back to path-derived name
|
|
612
|
+
const standardName = resolveSkillName(artifact);
|
|
440
613
|
|
|
441
614
|
// Clean up potential double extensions from source files (e.g. .yaml.md, .xml.md -> .md)
|
|
442
615
|
// This handles any extensions that might slip through toDashPath()
|
|
@@ -452,6 +625,80 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
452
625
|
return baseName.replace(/\.md$/, extension);
|
|
453
626
|
}
|
|
454
627
|
|
|
628
|
+
/**
|
|
629
|
+
* Install verbatim skill directories (type: skill entries from skill-manifest.csv).
|
|
630
|
+
* Copies the entire source directory as-is into the IDE skill directory.
|
|
631
|
+
* The source SKILL.md is used directly — no frontmatter transformation or file generation.
|
|
632
|
+
* @param {string} projectDir - Project directory
|
|
633
|
+
* @param {string} bmadDir - BMAD installation directory
|
|
634
|
+
* @param {string} targetPath - Target skills directory
|
|
635
|
+
* @param {Object} config - Installation configuration
|
|
636
|
+
* @returns {Promise<number>} Count of skills installed
|
|
637
|
+
*/
|
|
638
|
+
async installVerbatimSkills(projectDir, bmadDir, targetPath, config) {
|
|
639
|
+
const bmadFolderName = path.basename(bmadDir);
|
|
640
|
+
const bmadPrefix = bmadFolderName + '/';
|
|
641
|
+
const csvPath = path.join(bmadDir, '_config', 'skill-manifest.csv');
|
|
642
|
+
|
|
643
|
+
if (!(await fs.pathExists(csvPath))) return 0;
|
|
644
|
+
|
|
645
|
+
const csvContent = await fs.readFile(csvPath, 'utf8');
|
|
646
|
+
const records = csv.parse(csvContent, {
|
|
647
|
+
columns: true,
|
|
648
|
+
skip_empty_lines: true,
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
let count = 0;
|
|
652
|
+
|
|
653
|
+
for (const record of records) {
|
|
654
|
+
const canonicalId = record.canonicalId;
|
|
655
|
+
if (!canonicalId) continue;
|
|
656
|
+
|
|
657
|
+
// Derive source directory from path column
|
|
658
|
+
// path is like "_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/SKILL.md"
|
|
659
|
+
// Strip bmadFolderName prefix and join with bmadDir, then get dirname
|
|
660
|
+
const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path;
|
|
661
|
+
const sourceFile = path.join(bmadDir, relativePath);
|
|
662
|
+
const sourceDir = path.dirname(sourceFile);
|
|
663
|
+
|
|
664
|
+
if (!(await fs.pathExists(sourceDir))) continue;
|
|
665
|
+
|
|
666
|
+
// Clean target before copy to prevent stale files
|
|
667
|
+
const skillDir = path.join(targetPath, canonicalId);
|
|
668
|
+
await fs.remove(skillDir);
|
|
669
|
+
await fs.ensureDir(skillDir);
|
|
670
|
+
|
|
671
|
+
// Copy all skill files, filtering OS/editor artifacts recursively
|
|
672
|
+
const skipPatterns = new Set(['.DS_Store', 'Thumbs.db', 'desktop.ini']);
|
|
673
|
+
const skipSuffixes = ['~', '.swp', '.swo', '.bak'];
|
|
674
|
+
const filter = (src) => {
|
|
675
|
+
const name = path.basename(src);
|
|
676
|
+
if (src === sourceDir) return true;
|
|
677
|
+
if (skipPatterns.has(name)) return false;
|
|
678
|
+
if (name.startsWith('.') && name !== '.gitkeep') return false;
|
|
679
|
+
if (skipSuffixes.some((s) => name.endsWith(s))) return false;
|
|
680
|
+
return true;
|
|
681
|
+
};
|
|
682
|
+
await fs.copy(sourceDir, skillDir, { filter });
|
|
683
|
+
|
|
684
|
+
count++;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Post-install cleanup: remove _bmad/ directories for skills with install_to_bmad === "false"
|
|
688
|
+
for (const record of records) {
|
|
689
|
+
if (record.install_to_bmad === 'false') {
|
|
690
|
+
const relativePath = record.path.startsWith(bmadPrefix) ? record.path.slice(bmadPrefix.length) : record.path;
|
|
691
|
+
const sourceFile = path.join(bmadDir, relativePath);
|
|
692
|
+
const sourceDir = path.dirname(sourceFile);
|
|
693
|
+
if (await fs.pathExists(sourceDir)) {
|
|
694
|
+
await fs.remove(sourceDir);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return count;
|
|
700
|
+
}
|
|
701
|
+
|
|
455
702
|
/**
|
|
456
703
|
* Print installation summary
|
|
457
704
|
* @param {Object} results - Installation results
|
|
@@ -464,6 +711,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
464
711
|
if (results.workflows > 0) parts.push(`${results.workflows} workflows`);
|
|
465
712
|
if (results.tasks > 0) parts.push(`${results.tasks} tasks`);
|
|
466
713
|
if (results.tools > 0) parts.push(`${results.tools} tools`);
|
|
714
|
+
if (results.skills > 0) parts.push(`${results.skills} skills`);
|
|
467
715
|
await prompts.log.success(`${this.name} configured: ${parts.join(', ')} → ${targetDir}`);
|
|
468
716
|
}
|
|
469
717
|
|
|
@@ -476,11 +724,30 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
476
724
|
if (this.installerConfig?.legacy_targets) {
|
|
477
725
|
if (!options.silent) await prompts.log.message(' Migrating legacy directories...');
|
|
478
726
|
for (const legacyDir of this.installerConfig.legacy_targets) {
|
|
479
|
-
|
|
480
|
-
|
|
727
|
+
if (this.isGlobalPath(legacyDir)) {
|
|
728
|
+
await this.warnGlobalLegacy(legacyDir, options);
|
|
729
|
+
} else {
|
|
730
|
+
await this.cleanupTarget(projectDir, legacyDir, options);
|
|
731
|
+
await this.removeEmptyParents(projectDir, legacyDir);
|
|
732
|
+
}
|
|
481
733
|
}
|
|
482
734
|
}
|
|
483
735
|
|
|
736
|
+
// Strip BMAD markers from copilot-instructions.md if present
|
|
737
|
+
if (this.name === 'github-copilot') {
|
|
738
|
+
await this.cleanupCopilotInstructions(projectDir, options);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Strip BMAD modes from .kilocodemodes if present
|
|
742
|
+
if (this.name === 'kilo') {
|
|
743
|
+
await this.cleanupKiloModes(projectDir, options);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Strip BMAD entries from .rovodev/prompts.yml if present
|
|
747
|
+
if (this.name === 'rovo-dev') {
|
|
748
|
+
await this.cleanupRovoDevPrompts(projectDir, options);
|
|
749
|
+
}
|
|
750
|
+
|
|
484
751
|
// Clean all target directories
|
|
485
752
|
if (this.installerConfig?.targets) {
|
|
486
753
|
const parentDirs = new Set();
|
|
@@ -501,6 +768,41 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
501
768
|
}
|
|
502
769
|
}
|
|
503
770
|
|
|
771
|
+
/**
|
|
772
|
+
* Check if a path is global (starts with ~ or is absolute)
|
|
773
|
+
* @param {string} p - Path to check
|
|
774
|
+
* @returns {boolean}
|
|
775
|
+
*/
|
|
776
|
+
isGlobalPath(p) {
|
|
777
|
+
return p.startsWith('~') || path.isAbsolute(p);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Warn about stale BMAD files in a global legacy directory (never auto-deletes)
|
|
782
|
+
* @param {string} legacyDir - Legacy directory path (may start with ~)
|
|
783
|
+
* @param {Object} options - Options (silent, etc.)
|
|
784
|
+
*/
|
|
785
|
+
async warnGlobalLegacy(legacyDir, options = {}) {
|
|
786
|
+
try {
|
|
787
|
+
const expanded = legacyDir.startsWith('~/')
|
|
788
|
+
? path.join(os.homedir(), legacyDir.slice(2))
|
|
789
|
+
: legacyDir === '~'
|
|
790
|
+
? os.homedir()
|
|
791
|
+
: legacyDir;
|
|
792
|
+
|
|
793
|
+
if (!(await fs.pathExists(expanded))) return;
|
|
794
|
+
|
|
795
|
+
const entries = await fs.readdir(expanded);
|
|
796
|
+
const bmadFiles = entries.filter((e) => typeof e === 'string' && e.startsWith('bmad'));
|
|
797
|
+
|
|
798
|
+
if (bmadFiles.length > 0 && !options.silent) {
|
|
799
|
+
await prompts.log.warn(`Found ${bmadFiles.length} stale BMAD file(s) in ${expanded}. Remove manually: rm ${expanded}/bmad-*`);
|
|
800
|
+
}
|
|
801
|
+
} catch {
|
|
802
|
+
// Errors reading global paths are silently ignored
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
504
806
|
/**
|
|
505
807
|
* Cleanup a specific target directory
|
|
506
808
|
* @param {string} projectDir - Project directory
|
|
@@ -532,7 +834,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
532
834
|
if (!entry || typeof entry !== 'string') {
|
|
533
835
|
continue;
|
|
534
836
|
}
|
|
535
|
-
if (entry.startsWith('bmad')) {
|
|
837
|
+
if (entry.startsWith('bmad') && !entry.startsWith('bmad-os-')) {
|
|
536
838
|
const entryPath = path.join(targetPath, entry);
|
|
537
839
|
try {
|
|
538
840
|
await fs.remove(entryPath);
|
|
@@ -559,6 +861,121 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
559
861
|
}
|
|
560
862
|
}
|
|
561
863
|
}
|
|
864
|
+
/**
|
|
865
|
+
* Strip BMAD-owned content from .github/copilot-instructions.md.
|
|
866
|
+
* The old custom installer injected content between <!-- BMAD:START --> and <!-- BMAD:END --> markers.
|
|
867
|
+
* Deletes the file if nothing remains. Restores .bak backup if one exists.
|
|
868
|
+
*/
|
|
869
|
+
async cleanupCopilotInstructions(projectDir, options = {}) {
|
|
870
|
+
const filePath = path.join(projectDir, '.github', 'copilot-instructions.md');
|
|
871
|
+
|
|
872
|
+
if (!(await fs.pathExists(filePath))) return;
|
|
873
|
+
|
|
874
|
+
try {
|
|
875
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
876
|
+
const startIdx = content.indexOf('<!-- BMAD:START -->');
|
|
877
|
+
const endIdx = content.indexOf('<!-- BMAD:END -->');
|
|
878
|
+
|
|
879
|
+
if (startIdx === -1 || endIdx === -1 || endIdx <= startIdx) return;
|
|
880
|
+
|
|
881
|
+
const cleaned = content.slice(0, startIdx) + content.slice(endIdx + '<!-- BMAD:END -->'.length);
|
|
882
|
+
|
|
883
|
+
if (cleaned.trim().length === 0) {
|
|
884
|
+
await fs.remove(filePath);
|
|
885
|
+
const backupPath = `${filePath}.bak`;
|
|
886
|
+
if (await fs.pathExists(backupPath)) {
|
|
887
|
+
await fs.rename(backupPath, filePath);
|
|
888
|
+
if (!options.silent) await prompts.log.message(' Restored copilot-instructions.md from backup');
|
|
889
|
+
}
|
|
890
|
+
} else {
|
|
891
|
+
await fs.writeFile(filePath, cleaned, 'utf8');
|
|
892
|
+
const backupPath = `${filePath}.bak`;
|
|
893
|
+
if (await fs.pathExists(backupPath)) await fs.remove(backupPath);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
if (!options.silent) await prompts.log.message(' Cleaned BMAD markers from copilot-instructions.md');
|
|
897
|
+
} catch {
|
|
898
|
+
if (!options.silent) await prompts.log.warn(' Warning: Could not clean BMAD markers from copilot-instructions.md');
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Strip BMAD-owned modes from .kilocodemodes.
|
|
904
|
+
* The old custom kilo.js installer added modes with slug starting with 'bmad-'.
|
|
905
|
+
* Parses YAML, filters out BMAD modes, rewrites. Leaves file as-is on parse failure.
|
|
906
|
+
*/
|
|
907
|
+
async cleanupKiloModes(projectDir, options = {}) {
|
|
908
|
+
const kiloModesPath = path.join(projectDir, '.kilocodemodes');
|
|
909
|
+
|
|
910
|
+
if (!(await fs.pathExists(kiloModesPath))) return;
|
|
911
|
+
|
|
912
|
+
const content = await fs.readFile(kiloModesPath, 'utf8');
|
|
913
|
+
|
|
914
|
+
let config;
|
|
915
|
+
try {
|
|
916
|
+
config = yaml.parse(content) || {};
|
|
917
|
+
} catch {
|
|
918
|
+
if (!options.silent) await prompts.log.warn(' Warning: Could not parse .kilocodemodes for cleanup');
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (!Array.isArray(config.customModes)) return;
|
|
923
|
+
|
|
924
|
+
const originalCount = config.customModes.length;
|
|
925
|
+
config.customModes = config.customModes.filter((mode) => mode && (!mode.slug || !mode.slug.startsWith('bmad-')));
|
|
926
|
+
const removedCount = originalCount - config.customModes.length;
|
|
927
|
+
|
|
928
|
+
if (removedCount > 0) {
|
|
929
|
+
try {
|
|
930
|
+
await fs.writeFile(kiloModesPath, yaml.stringify(config, { lineWidth: 0 }));
|
|
931
|
+
if (!options.silent) await prompts.log.message(` Removed ${removedCount} BMAD modes from .kilocodemodes`);
|
|
932
|
+
} catch {
|
|
933
|
+
if (!options.silent) await prompts.log.warn(' Warning: Could not write .kilocodemodes during cleanup');
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/**
|
|
939
|
+
* Strip BMAD-owned entries from .rovodev/prompts.yml.
|
|
940
|
+
* The old custom rovodev.js installer registered workflows in prompts.yml.
|
|
941
|
+
* Parses YAML, filters out entries with name starting with 'bmad-', rewrites.
|
|
942
|
+
* Removes the file if no entries remain.
|
|
943
|
+
*/
|
|
944
|
+
async cleanupRovoDevPrompts(projectDir, options = {}) {
|
|
945
|
+
const promptsPath = path.join(projectDir, '.rovodev', 'prompts.yml');
|
|
946
|
+
|
|
947
|
+
if (!(await fs.pathExists(promptsPath))) return;
|
|
948
|
+
|
|
949
|
+
const content = await fs.readFile(promptsPath, 'utf8');
|
|
950
|
+
|
|
951
|
+
let config;
|
|
952
|
+
try {
|
|
953
|
+
config = yaml.parse(content) || {};
|
|
954
|
+
} catch {
|
|
955
|
+
if (!options.silent) await prompts.log.warn(' Warning: Could not parse prompts.yml for cleanup');
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (!Array.isArray(config.prompts)) return;
|
|
960
|
+
|
|
961
|
+
const originalCount = config.prompts.length;
|
|
962
|
+
config.prompts = config.prompts.filter((entry) => entry && (!entry.name || !entry.name.startsWith('bmad-')));
|
|
963
|
+
const removedCount = originalCount - config.prompts.length;
|
|
964
|
+
|
|
965
|
+
if (removedCount > 0) {
|
|
966
|
+
try {
|
|
967
|
+
if (config.prompts.length === 0) {
|
|
968
|
+
await fs.remove(promptsPath);
|
|
969
|
+
} else {
|
|
970
|
+
await fs.writeFile(promptsPath, yaml.stringify(config, { lineWidth: 0 }));
|
|
971
|
+
}
|
|
972
|
+
if (!options.silent) await prompts.log.message(` Removed ${removedCount} BMAD entries from prompts.yml`);
|
|
973
|
+
} catch {
|
|
974
|
+
if (!options.silent) await prompts.log.warn(' Warning: Could not write prompts.yml during cleanup');
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
562
979
|
/**
|
|
563
980
|
* Check ancestor directories for existing BMAD files in the same target_dir.
|
|
564
981
|
* IDEs like Claude Code inherit commands from parent directories, so an existing
|
|
@@ -579,7 +996,9 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
579
996
|
try {
|
|
580
997
|
if (await fs.pathExists(candidatePath)) {
|
|
581
998
|
const entries = await fs.readdir(candidatePath);
|
|
582
|
-
const hasBmad = entries.some(
|
|
999
|
+
const hasBmad = entries.some(
|
|
1000
|
+
(e) => typeof e === 'string' && e.toLowerCase().startsWith('bmad') && !e.toLowerCase().startsWith('bmad-os-'),
|
|
1001
|
+
);
|
|
583
1002
|
if (hasBmad) {
|
|
584
1003
|
return candidatePath;
|
|
585
1004
|
}
|