bmad-method 6.0.0-Beta.1 → 6.0.0-Beta.3
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/CHANGELOG.md +35 -1
- package/README.md +41 -9
- package/docs/how-to/customize-bmad.md +1 -1
- package/docs/reference/workflow-map.md +3 -0
- package/package.json +1 -1
- package/src/bmm/agents/quinn.agent.yaml +57 -0
- package/src/bmm/module-help.csv +31 -31
- package/src/bmm/teams/default-party.csv +0 -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-01b-legacy-conversion.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-e/step-e-03-edit.md +1 -1
- package/src/bmm/workflows/4-implementation/create-story/instructions.xml +1 -1
- package/src/bmm/workflows/4-implementation/dev-story/instructions.xml +1 -1
- package/src/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +1 -1
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +1 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +1 -1
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +1 -1
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +1 -1
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +1 -1
- package/src/bmm/workflows/qa/automate/checklist.md +33 -0
- package/src/bmm/workflows/qa/automate/instructions.md +110 -0
- package/src/bmm/workflows/qa/automate/workflow.yaml +49 -0
- package/src/core/module-help.csv +8 -8
- package/test/test-installation-components.js +9 -11
- package/tools/cli/external-official-modules.yaml +10 -0
- package/tools/cli/installers/lib/core/installer.js +26 -40
- package/tools/cli/installers/lib/ide/_config-driven.js +450 -0
- package/tools/cli/installers/lib/ide/codex.js +40 -12
- package/tools/cli/installers/lib/ide/manager.js +65 -38
- package/tools/cli/installers/lib/ide/platform-codes.js +100 -0
- package/tools/cli/installers/lib/ide/platform-codes.yaml +227 -0
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +21 -5
- package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +5 -0
- package/tools/cli/installers/lib/ide/shared/path-utils.js +177 -50
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +7 -5
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +29 -3
- package/tools/cli/installers/lib/ide/templates/combined/antigravity.md +8 -0
- package/tools/cli/installers/lib/ide/templates/combined/default-agent.md +15 -0
- package/tools/cli/installers/lib/ide/templates/combined/default-workflow-yaml.md +14 -0
- package/tools/cli/installers/lib/ide/templates/combined/default-workflow.md +6 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-agent.toml +14 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-workflow-yaml.toml +16 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-workflow.toml +14 -0
- package/tools/cli/installers/lib/ide/templates/combined/rovodev.md +9 -0
- package/tools/cli/installers/lib/ide/templates/combined/trae.md +9 -0
- package/tools/cli/installers/lib/ide/templates/combined/windsurf-workflow.md +10 -0
- package/tools/cli/installers/lib/ide/templates/split/opencode/body.md +10 -0
- package/tools/cli/installers/lib/ide/templates/split/opencode/header.md +4 -0
- package/tools/cli/lib/ui.js +19 -75
- package/docs/tea/explanation/engagement-models.md +0 -710
- package/docs/tea/explanation/fixture-architecture.md +0 -457
- package/docs/tea/explanation/knowledge-base-system.md +0 -554
- package/docs/tea/explanation/network-first-patterns.md +0 -853
- package/docs/tea/explanation/risk-based-testing.md +0 -586
- package/docs/tea/explanation/tea-overview.md +0 -410
- package/docs/tea/explanation/test-quality-standards.md +0 -907
- package/docs/tea/explanation/testing-as-engineering.md +0 -112
- package/docs/tea/glossary/index.md +0 -159
- package/docs/tea/how-to/brownfield/use-tea-for-enterprise.md +0 -525
- package/docs/tea/how-to/brownfield/use-tea-with-existing-tests.md +0 -577
- package/docs/tea/how-to/customization/enable-tea-mcp-enhancements.md +0 -424
- package/docs/tea/how-to/customization/integrate-playwright-utils.md +0 -813
- package/docs/tea/how-to/workflows/run-atdd.md +0 -436
- package/docs/tea/how-to/workflows/run-automate.md +0 -653
- package/docs/tea/how-to/workflows/run-nfr-assess.md +0 -679
- package/docs/tea/how-to/workflows/run-test-design.md +0 -135
- package/docs/tea/how-to/workflows/run-test-review.md +0 -605
- package/docs/tea/how-to/workflows/run-trace.md +0 -883
- package/docs/tea/how-to/workflows/setup-ci.md +0 -712
- package/docs/tea/how-to/workflows/setup-test-framework.md +0 -98
- package/docs/tea/reference/commands.md +0 -276
- package/docs/tea/reference/configuration.md +0 -678
- package/docs/tea/reference/knowledge-base.md +0 -340
- package/docs/tea/tutorials/tea-lite-quickstart.md +0 -444
- package/src/bmm/agents/tea.agent.yaml +0 -63
- package/src/bmm/testarch/knowledge/adr-quality-readiness-checklist.md +0 -350
- package/src/bmm/testarch/knowledge/api-request.md +0 -442
- package/src/bmm/testarch/knowledge/api-testing-patterns.md +0 -843
- package/src/bmm/testarch/knowledge/auth-session.md +0 -552
- package/src/bmm/testarch/knowledge/burn-in.md +0 -273
- package/src/bmm/testarch/knowledge/ci-burn-in.md +0 -675
- package/src/bmm/testarch/knowledge/component-tdd.md +0 -486
- package/src/bmm/testarch/knowledge/contract-testing.md +0 -957
- package/src/bmm/testarch/knowledge/data-factories.md +0 -500
- package/src/bmm/testarch/knowledge/email-auth.md +0 -721
- package/src/bmm/testarch/knowledge/error-handling.md +0 -725
- package/src/bmm/testarch/knowledge/feature-flags.md +0 -750
- package/src/bmm/testarch/knowledge/file-utils.md +0 -463
- package/src/bmm/testarch/knowledge/fixture-architecture.md +0 -401
- package/src/bmm/testarch/knowledge/fixtures-composition.md +0 -382
- package/src/bmm/testarch/knowledge/intercept-network-call.md +0 -430
- package/src/bmm/testarch/knowledge/log.md +0 -429
- package/src/bmm/testarch/knowledge/network-error-monitor.md +0 -405
- package/src/bmm/testarch/knowledge/network-first.md +0 -486
- package/src/bmm/testarch/knowledge/network-recorder.md +0 -527
- package/src/bmm/testarch/knowledge/nfr-criteria.md +0 -670
- package/src/bmm/testarch/knowledge/overview.md +0 -286
- package/src/bmm/testarch/knowledge/playwright-config.md +0 -730
- package/src/bmm/testarch/knowledge/probability-impact.md +0 -601
- package/src/bmm/testarch/knowledge/recurse.md +0 -421
- package/src/bmm/testarch/knowledge/risk-governance.md +0 -615
- package/src/bmm/testarch/knowledge/selective-testing.md +0 -732
- package/src/bmm/testarch/knowledge/selector-resilience.md +0 -527
- package/src/bmm/testarch/knowledge/test-healing-patterns.md +0 -644
- package/src/bmm/testarch/knowledge/test-levels-framework.md +0 -473
- package/src/bmm/testarch/knowledge/test-priorities-matrix.md +0 -373
- package/src/bmm/testarch/knowledge/test-quality.md +0 -664
- package/src/bmm/testarch/knowledge/timing-debugging.md +0 -372
- package/src/bmm/testarch/knowledge/visual-debugging.md +0 -524
- package/src/bmm/testarch/tea-index.csv +0 -35
- package/src/bmm/workflows/testarch/atdd/atdd-checklist-template.md +0 -363
- package/src/bmm/workflows/testarch/atdd/checklist.md +0 -374
- package/src/bmm/workflows/testarch/atdd/instructions.md +0 -806
- package/src/bmm/workflows/testarch/atdd/workflow.yaml +0 -47
- package/src/bmm/workflows/testarch/automate/checklist.md +0 -582
- package/src/bmm/workflows/testarch/automate/instructions.md +0 -1324
- package/src/bmm/workflows/testarch/automate/workflow.yaml +0 -54
- package/src/bmm/workflows/testarch/ci/checklist.md +0 -247
- package/src/bmm/workflows/testarch/ci/github-actions-template.yaml +0 -198
- package/src/bmm/workflows/testarch/ci/gitlab-ci-template.yaml +0 -149
- package/src/bmm/workflows/testarch/ci/instructions.md +0 -536
- package/src/bmm/workflows/testarch/ci/workflow.yaml +0 -47
- package/src/bmm/workflows/testarch/framework/checklist.md +0 -320
- package/src/bmm/workflows/testarch/framework/instructions.md +0 -481
- package/src/bmm/workflows/testarch/framework/workflow.yaml +0 -49
- package/src/bmm/workflows/testarch/nfr-assess/checklist.md +0 -407
- package/src/bmm/workflows/testarch/nfr-assess/instructions.md +0 -726
- package/src/bmm/workflows/testarch/nfr-assess/nfr-report-template.md +0 -461
- package/src/bmm/workflows/testarch/nfr-assess/workflow.yaml +0 -49
- package/src/bmm/workflows/testarch/test-design/checklist.md +0 -407
- package/src/bmm/workflows/testarch/test-design/instructions.md +0 -1158
- package/src/bmm/workflows/testarch/test-design/test-design-architecture-template.md +0 -213
- package/src/bmm/workflows/testarch/test-design/test-design-qa-template.md +0 -286
- package/src/bmm/workflows/testarch/test-design/test-design-template.md +0 -294
- package/src/bmm/workflows/testarch/test-design/workflow.yaml +0 -71
- package/src/bmm/workflows/testarch/test-review/checklist.md +0 -472
- package/src/bmm/workflows/testarch/test-review/instructions.md +0 -628
- package/src/bmm/workflows/testarch/test-review/test-review-template.md +0 -390
- package/src/bmm/workflows/testarch/test-review/workflow.yaml +0 -48
- package/src/bmm/workflows/testarch/trace/checklist.md +0 -642
- package/src/bmm/workflows/testarch/trace/instructions.md +0 -1030
- package/src/bmm/workflows/testarch/trace/trace-template.md +0 -675
- package/src/bmm/workflows/testarch/trace/workflow.yaml +0 -57
- package/tools/cli/installers/lib/ide/STANDARDIZATION_PLAN.md +0 -208
- package/tools/cli/installers/lib/ide/antigravity.js +0 -474
- package/tools/cli/installers/lib/ide/auggie.js +0 -244
- package/tools/cli/installers/lib/ide/claude-code.js +0 -506
- package/tools/cli/installers/lib/ide/cline.js +0 -272
- package/tools/cli/installers/lib/ide/crush.js +0 -149
- package/tools/cli/installers/lib/ide/cursor.js +0 -160
- package/tools/cli/installers/lib/ide/gemini.js +0 -301
- package/tools/cli/installers/lib/ide/github-copilot.js +0 -383
- package/tools/cli/installers/lib/ide/iflow.js +0 -191
- package/tools/cli/installers/lib/ide/opencode.js +0 -257
- package/tools/cli/installers/lib/ide/qwen.js +0 -372
- package/tools/cli/installers/lib/ide/roo.js +0 -273
- package/tools/cli/installers/lib/ide/rovo-dev.js +0 -290
- package/tools/cli/installers/lib/ide/templates/gemini-agent-command.toml +0 -14
- package/tools/cli/installers/lib/ide/templates/gemini-task-command.toml +0 -12
- package/tools/cli/installers/lib/ide/trae.js +0 -313
- package/tools/cli/installers/lib/ide/windsurf.js +0 -258
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Quinn QA workflow: Automate
|
|
2
|
+
name: qa-automate
|
|
3
|
+
description: "Generate tests quickly for existing features using standard test patterns"
|
|
4
|
+
author: "BMad"
|
|
5
|
+
|
|
6
|
+
# Critical variables from config
|
|
7
|
+
config_source: "{project-root}/_bmad/bmm/config.yaml"
|
|
8
|
+
output_folder: "{config_source}:output_folder"
|
|
9
|
+
implementation_artifacts: "{config_source}:implementation_artifacts"
|
|
10
|
+
user_name: "{config_source}:user_name"
|
|
11
|
+
communication_language: "{config_source}:communication_language"
|
|
12
|
+
document_output_language: "{config_source}:document_output_language"
|
|
13
|
+
date: system-generated
|
|
14
|
+
|
|
15
|
+
# Workflow components
|
|
16
|
+
installed_path: "{project-root}/_bmad/bmm/workflows/qa/automate"
|
|
17
|
+
instructions: "{installed_path}/instructions.md"
|
|
18
|
+
validation: "{installed_path}/checklist.md"
|
|
19
|
+
template: false
|
|
20
|
+
|
|
21
|
+
# Variables and inputs
|
|
22
|
+
variables:
|
|
23
|
+
# Directory paths
|
|
24
|
+
test_dir: "{project-root}/tests" # Root test directory
|
|
25
|
+
source_dir: "{project-root}" # Source code directory
|
|
26
|
+
|
|
27
|
+
# Output configuration
|
|
28
|
+
default_output_file: "{implementation_artifacts}/tests/test-summary.md"
|
|
29
|
+
|
|
30
|
+
# Required tools
|
|
31
|
+
required_tools:
|
|
32
|
+
- read_file # Read source code and existing tests
|
|
33
|
+
- write_file # Create test files
|
|
34
|
+
- create_directory # Create test directories
|
|
35
|
+
- list_files # Discover features
|
|
36
|
+
- search_repo # Find patterns
|
|
37
|
+
- glob # Find files
|
|
38
|
+
|
|
39
|
+
tags:
|
|
40
|
+
- qa
|
|
41
|
+
- automation
|
|
42
|
+
- testing
|
|
43
|
+
|
|
44
|
+
execution_hints:
|
|
45
|
+
interactive: false
|
|
46
|
+
autonomous: true
|
|
47
|
+
iterative: false
|
|
48
|
+
|
|
49
|
+
web_bundle: false
|
package/src/core/module-help.csv
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module,phase,name,code,sequence,workflow-file,command,required,agent,options,description,output-location,outputs
|
|
2
|
-
core,,Brainstorming,BS,20,_bmad/core/workflows/brainstorming/workflow.md,
|
|
3
|
-
core,,Party Mode,PM,30,_bmad/core/workflows/party-mode/workflow.md,
|
|
4
|
-
core,,bmad-help,BH,40,_bmad/core/tasks/help.md,
|
|
5
|
-
core,,Index Docs,ID,50,_bmad/core/tasks/index-docs.xml,
|
|
6
|
-
core,,Shard Document,SD,70,_bmad/core/tasks/shard-doc.xml,
|
|
7
|
-
core,,Editorial Review - Prose,EP,80,_bmad/core/tasks/editorial-review-prose.xml,
|
|
8
|
-
core,,Editorial Review - Structure,ES,90,_bmad/core/tasks/editorial-review-structure.xml,
|
|
9
|
-
core,,Adversarial Review (General),AR,100,_bmad/core/tasks/review-adversarial-general.xml,
|
|
2
|
+
core,,Brainstorming,BS,20,_bmad/core/workflows/brainstorming/workflow.md,bmad-brainstorming,false,analyst,,Facilitate interactive brainstorming sessions using diverse creative techniques and ideation methods,{output_folder}/brainstorming/brainstorming-session-{{date}}.md,,
|
|
3
|
+
core,,Party Mode,PM,30,_bmad/core/workflows/party-mode/workflow.md,bmad-party-mode,false,party-mode facilitator,,Orchestrates group discussions between all installed BMAD agents enabling natural multi-agent conversations,,
|
|
4
|
+
core,,bmad-help,BH,40,_bmad/core/tasks/help.md,bmad-help,false,,,Get unstuck by showing what workflow steps come next or answering questions about what to do in the BMad Method,,
|
|
5
|
+
core,,Index Docs,ID,50,_bmad/core/tasks/index-docs.xml,bmad-index-docs,false,,,Generates or updates an index.md of all documents in the specified directory,,
|
|
6
|
+
core,,Shard Document,SD,70,_bmad/core/tasks/shard-doc.xml,bmad-shard-doc,false,,,Splits large markdown documents into smaller organized files based on level 2 sections,,
|
|
7
|
+
core,,Editorial Review - Prose,EP,80,_bmad/core/tasks/editorial-review-prose.xml,bmad-editorial-review-prose,false,,,Clinical copy-editor that reviews text for communication issues,,"three-column markdown table with suggested fixes",
|
|
8
|
+
core,,Editorial Review - Structure,ES,90,_bmad/core/tasks/editorial-review-structure.xml,bmad-editorial-review-structure,false,,,Structural editor that proposes cuts reorganization and simplification while preserving comprehension,,
|
|
9
|
+
core,,Adversarial Review (General),AR,100,_bmad/core/tasks/review-adversarial-general.xml,bmad-review-adversarial-general,false,,,Cynically review content and produce findings,,
|
|
@@ -158,32 +158,30 @@ async function runTests() {
|
|
|
158
158
|
console.log('');
|
|
159
159
|
|
|
160
160
|
// ============================================================
|
|
161
|
-
// Test 5:
|
|
161
|
+
// Test 5: QA Agent Compilation
|
|
162
162
|
// ============================================================
|
|
163
|
-
console.log(`${colors.yellow}Test Suite 5:
|
|
163
|
+
console.log(`${colors.yellow}Test Suite 5: QA Agent Compilation${colors.reset}\n`);
|
|
164
164
|
|
|
165
165
|
try {
|
|
166
166
|
const builder = new YamlXmlBuilder();
|
|
167
|
-
const
|
|
168
|
-
const tempOutput = path.join(__dirname, 'temp-
|
|
167
|
+
const qaAgentPath = path.join(projectRoot, 'src/bmm/agents/quinn.agent.yaml');
|
|
168
|
+
const tempOutput = path.join(__dirname, 'temp-qa-agent.md');
|
|
169
169
|
|
|
170
170
|
try {
|
|
171
|
-
const result = await builder.buildAgent(
|
|
171
|
+
const result = await builder.buildAgent(qaAgentPath, null, tempOutput, { includeMetadata: true });
|
|
172
172
|
const compiled = await fs.readFile(tempOutput, 'utf8');
|
|
173
173
|
|
|
174
|
-
assert(compiled.includes('
|
|
174
|
+
assert(compiled.includes('QA Engineer'), 'QA agent compilation includes agent title');
|
|
175
175
|
|
|
176
|
-
assert(compiled.includes('
|
|
177
|
-
|
|
178
|
-
assert(compiled.includes('test-design'), 'TEA agent menu includes test-design workflow');
|
|
176
|
+
assert(compiled.includes('qa/automate'), 'QA agent menu includes automate workflow');
|
|
179
177
|
|
|
180
178
|
// Cleanup
|
|
181
179
|
await fs.remove(tempOutput);
|
|
182
180
|
} catch (error) {
|
|
183
|
-
assert(false, '
|
|
181
|
+
assert(false, 'QA agent compiles successfully', error.message);
|
|
184
182
|
}
|
|
185
183
|
} catch (error) {
|
|
186
|
-
assert(false, '
|
|
184
|
+
assert(false, 'QA compilation test setup', error.message);
|
|
187
185
|
}
|
|
188
186
|
|
|
189
187
|
console.log('');
|
|
@@ -32,6 +32,16 @@ modules:
|
|
|
32
32
|
type: bmad-org
|
|
33
33
|
npmPackage: bmad-game-dev-studio
|
|
34
34
|
|
|
35
|
+
bmad-method-test-architecture-enterprise:
|
|
36
|
+
url: https://github.com/bmad-code-org/bmad-method-test-architecture-enterprise
|
|
37
|
+
module-definition: src/module.yaml
|
|
38
|
+
code: tea
|
|
39
|
+
name: "Test Architect"
|
|
40
|
+
description: "Master Test Architect for quality strategy, test automation, and release gates"
|
|
41
|
+
defaultSelected: false
|
|
42
|
+
type: bmad-org
|
|
43
|
+
npmPackage: bmad-method-test-architecture-enterprise
|
|
44
|
+
|
|
35
45
|
# TODO: Enable once fixes applied:
|
|
36
46
|
|
|
37
47
|
# whiteport-design-system:
|
|
@@ -161,56 +161,39 @@ class Installer {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
if (!toolConfig.skipIde && toolConfig.ides && toolConfig.ides.length > 0) {
|
|
164
|
+
// Ensure IDE manager is initialized
|
|
165
|
+
await this.ideManager.ensureInitialized();
|
|
166
|
+
|
|
164
167
|
// Determine which IDEs are newly selected (not previously configured)
|
|
165
168
|
const newlySelectedIdes = toolConfig.ides.filter((ide) => !previouslyConfiguredIdes.includes(ide));
|
|
166
169
|
|
|
167
170
|
if (newlySelectedIdes.length > 0) {
|
|
168
171
|
console.log('\n'); // Add spacing before IDE questions
|
|
169
172
|
|
|
173
|
+
// Collect configuration for IDEs that support it
|
|
170
174
|
for (const ide of newlySelectedIdes) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const needsPrompts = ['claude-code', 'github-copilot', 'roo', 'cline', 'auggie', 'codex', 'qwen', 'gemini', 'rovo-dev'].includes(
|
|
174
|
-
ide,
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
if (needsPrompts) {
|
|
178
|
-
// Get IDE handler and collect configuration
|
|
179
|
-
try {
|
|
180
|
-
// Dynamically load the IDE setup module
|
|
181
|
-
const ideModule = require(`../ide/${ide}`);
|
|
182
|
-
|
|
183
|
-
// Get the setup class (handle different export formats)
|
|
184
|
-
let SetupClass;
|
|
185
|
-
const className =
|
|
186
|
-
ide
|
|
187
|
-
.split('-')
|
|
188
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
189
|
-
.join('') + 'Setup';
|
|
190
|
-
|
|
191
|
-
if (ideModule[className]) {
|
|
192
|
-
SetupClass = ideModule[className];
|
|
193
|
-
} else if (ideModule.default) {
|
|
194
|
-
SetupClass = ideModule.default;
|
|
195
|
-
} else {
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
175
|
+
try {
|
|
176
|
+
const handler = this.ideManager.handlers.get(ide);
|
|
198
177
|
|
|
199
|
-
|
|
178
|
+
if (!handler) {
|
|
179
|
+
console.warn(chalk.yellow(`Warning: IDE '${ide}' handler not found`));
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
200
182
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
} catch {
|
|
211
|
-
// IDE doesn't have a setup file or collectConfiguration method
|
|
212
|
-
console.warn(chalk.yellow(`Warning: Could not load configuration for ${ide}`));
|
|
183
|
+
// Check if this IDE handler has a collectConfiguration method
|
|
184
|
+
// (custom installers like Codex, Kilo, Kiro-cli may have this)
|
|
185
|
+
if (typeof handler.collectConfiguration === 'function') {
|
|
186
|
+
console.log(chalk.cyan(`\nConfiguring ${ide}...`));
|
|
187
|
+
ideConfigurations[ide] = await handler.collectConfiguration({
|
|
188
|
+
selectedModules: selectedModules || [],
|
|
189
|
+
projectDir,
|
|
190
|
+
bmadDir,
|
|
191
|
+
});
|
|
213
192
|
}
|
|
193
|
+
// Most config-driven IDEs don't need configuration - silently skip
|
|
194
|
+
} catch (error) {
|
|
195
|
+
// IDE doesn't support configuration or has an error
|
|
196
|
+
console.warn(chalk.yellow(`Warning: Could not load configuration for ${ide}: ${error.message}`));
|
|
214
197
|
}
|
|
215
198
|
}
|
|
216
199
|
}
|
|
@@ -1016,6 +999,9 @@ class Installer {
|
|
|
1016
999
|
|
|
1017
1000
|
// Configure IDEs and copy documentation
|
|
1018
1001
|
if (!config.skipIde && config.ides && config.ides.length > 0) {
|
|
1002
|
+
// Ensure IDE manager is initialized (handlers may not be loaded in quick update flow)
|
|
1003
|
+
await this.ideManager.ensureInitialized();
|
|
1004
|
+
|
|
1019
1005
|
// Filter out any undefined/null values from the IDE list
|
|
1020
1006
|
const validIdes = config.ides.filter((ide) => ide && typeof ide === 'string');
|
|
1021
1007
|
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
const path = require('node:path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { BaseIdeSetup } = require('./_base-ide');
|
|
5
|
+
const { AgentCommandGenerator } = require('./shared/agent-command-generator');
|
|
6
|
+
const { WorkflowCommandGenerator } = require('./shared/workflow-command-generator');
|
|
7
|
+
const { TaskToolCommandGenerator } = require('./shared/task-tool-command-generator');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Config-driven IDE setup handler
|
|
11
|
+
*
|
|
12
|
+
* This class provides a standardized way to install BMAD artifacts to IDEs
|
|
13
|
+
* based on configuration in platform-codes.yaml. It eliminates the need for
|
|
14
|
+
* individual installer files for each IDE.
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Config-driven from platform-codes.yaml
|
|
18
|
+
* - Template-based content generation
|
|
19
|
+
* - Multi-target installation support (e.g., GitHub Copilot)
|
|
20
|
+
* - Artifact type filtering (agents, workflows, tasks, tools)
|
|
21
|
+
*/
|
|
22
|
+
class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
23
|
+
constructor(platformCode, platformConfig) {
|
|
24
|
+
super(platformCode, platformConfig.name, platformConfig.preferred);
|
|
25
|
+
this.platformConfig = platformConfig;
|
|
26
|
+
this.installerConfig = platformConfig.installer || null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Main setup method - called by IdeManager
|
|
31
|
+
* @param {string} projectDir - Project directory
|
|
32
|
+
* @param {string} bmadDir - BMAD installation directory
|
|
33
|
+
* @param {Object} options - Setup options
|
|
34
|
+
* @returns {Promise<Object>} Setup result
|
|
35
|
+
*/
|
|
36
|
+
async setup(projectDir, bmadDir, options = {}) {
|
|
37
|
+
console.log(chalk.cyan(`Setting up ${this.name}...`));
|
|
38
|
+
|
|
39
|
+
// Clean up any old BMAD installation first
|
|
40
|
+
await this.cleanup(projectDir);
|
|
41
|
+
|
|
42
|
+
if (!this.installerConfig) {
|
|
43
|
+
return { success: false, reason: 'no-config' };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Handle multi-target installations (e.g., GitHub Copilot)
|
|
47
|
+
if (this.installerConfig.targets) {
|
|
48
|
+
return this.installToMultipleTargets(projectDir, bmadDir, this.installerConfig.targets, options);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle single-target installations
|
|
52
|
+
if (this.installerConfig.target_dir) {
|
|
53
|
+
return this.installToTarget(projectDir, bmadDir, this.installerConfig, options);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { success: false, reason: 'invalid-config' };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Install to a single target directory
|
|
61
|
+
* @param {string} projectDir - Project directory
|
|
62
|
+
* @param {string} bmadDir - BMAD installation directory
|
|
63
|
+
* @param {Object} config - Installation configuration
|
|
64
|
+
* @param {Object} options - Setup options
|
|
65
|
+
* @returns {Promise<Object>} Installation result
|
|
66
|
+
*/
|
|
67
|
+
async installToTarget(projectDir, bmadDir, config, options) {
|
|
68
|
+
const { target_dir, template_type, artifact_types } = config;
|
|
69
|
+
const targetPath = path.join(projectDir, target_dir);
|
|
70
|
+
await this.ensureDir(targetPath);
|
|
71
|
+
|
|
72
|
+
const selectedModules = options.selectedModules || [];
|
|
73
|
+
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0 };
|
|
74
|
+
|
|
75
|
+
// Install agents
|
|
76
|
+
if (!artifact_types || artifact_types.includes('agents')) {
|
|
77
|
+
const agentGen = new AgentCommandGenerator(this.bmadFolderName);
|
|
78
|
+
const { artifacts } = await agentGen.collectAgentArtifacts(bmadDir, selectedModules);
|
|
79
|
+
results.agents = await this.writeAgentArtifacts(targetPath, artifacts, template_type, config);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Install workflows
|
|
83
|
+
if (!artifact_types || artifact_types.includes('workflows')) {
|
|
84
|
+
const workflowGen = new WorkflowCommandGenerator(this.bmadFolderName);
|
|
85
|
+
const { artifacts } = await workflowGen.collectWorkflowArtifacts(bmadDir);
|
|
86
|
+
results.workflows = await this.writeWorkflowArtifacts(targetPath, artifacts, template_type, config);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Install tasks and tools
|
|
90
|
+
if (!artifact_types || artifact_types.includes('tasks') || artifact_types.includes('tools')) {
|
|
91
|
+
const taskToolGen = new TaskToolCommandGenerator();
|
|
92
|
+
const taskToolResult = await taskToolGen.generateDashTaskToolCommands(projectDir, bmadDir, targetPath);
|
|
93
|
+
results.tasks = taskToolResult.tasks || 0;
|
|
94
|
+
results.tools = taskToolResult.tools || 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.printSummary(results, target_dir);
|
|
98
|
+
return { success: true, results };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Install to multiple target directories
|
|
103
|
+
* @param {string} projectDir - Project directory
|
|
104
|
+
* @param {string} bmadDir - BMAD installation directory
|
|
105
|
+
* @param {Array} targets - Array of target configurations
|
|
106
|
+
* @param {Object} options - Setup options
|
|
107
|
+
* @returns {Promise<Object>} Installation result
|
|
108
|
+
*/
|
|
109
|
+
async installToMultipleTargets(projectDir, bmadDir, targets, options) {
|
|
110
|
+
const allResults = { agents: 0, workflows: 0, tasks: 0, tools: 0 };
|
|
111
|
+
|
|
112
|
+
for (const target of targets) {
|
|
113
|
+
const result = await this.installToTarget(projectDir, bmadDir, target, options);
|
|
114
|
+
if (result.success) {
|
|
115
|
+
allResults.agents += result.results.agents || 0;
|
|
116
|
+
allResults.workflows += result.results.workflows || 0;
|
|
117
|
+
allResults.tasks += result.results.tasks || 0;
|
|
118
|
+
allResults.tools += result.results.tools || 0;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return { success: true, results: allResults };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Write agent artifacts to target directory
|
|
127
|
+
* @param {string} targetPath - Target directory path
|
|
128
|
+
* @param {Array} artifacts - Agent artifacts
|
|
129
|
+
* @param {string} templateType - Template type to use
|
|
130
|
+
* @param {Object} config - Installation configuration
|
|
131
|
+
* @returns {Promise<number>} Count of artifacts written
|
|
132
|
+
*/
|
|
133
|
+
async writeAgentArtifacts(targetPath, artifacts, templateType, config = {}) {
|
|
134
|
+
// Try to load platform-specific template, fall back to default-agent
|
|
135
|
+
const { content: template, extension } = await this.loadTemplate(templateType, 'agent', config, 'default-agent');
|
|
136
|
+
let count = 0;
|
|
137
|
+
|
|
138
|
+
for (const artifact of artifacts) {
|
|
139
|
+
const content = this.renderTemplate(template, artifact);
|
|
140
|
+
const filename = this.generateFilename(artifact, 'agent', extension);
|
|
141
|
+
const filePath = path.join(targetPath, filename);
|
|
142
|
+
await this.writeFile(filePath, content);
|
|
143
|
+
count++;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return count;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Write workflow artifacts to target directory
|
|
151
|
+
* @param {string} targetPath - Target directory path
|
|
152
|
+
* @param {Array} artifacts - Workflow artifacts
|
|
153
|
+
* @param {string} templateType - Template type to use
|
|
154
|
+
* @param {Object} config - Installation configuration
|
|
155
|
+
* @returns {Promise<number>} Count of artifacts written
|
|
156
|
+
*/
|
|
157
|
+
async writeWorkflowArtifacts(targetPath, artifacts, templateType, config = {}) {
|
|
158
|
+
let count = 0;
|
|
159
|
+
|
|
160
|
+
for (const artifact of artifacts) {
|
|
161
|
+
if (artifact.type === 'workflow-command') {
|
|
162
|
+
// Use different template based on workflow type (YAML vs MD)
|
|
163
|
+
// Default to 'default' template type, but allow override via config
|
|
164
|
+
const workflowTemplateType = artifact.isYamlWorkflow
|
|
165
|
+
? config.yaml_workflow_template || `${templateType}-workflow-yaml`
|
|
166
|
+
: config.md_workflow_template || `${templateType}-workflow`;
|
|
167
|
+
|
|
168
|
+
// Fall back to default templates if specific ones don't exist
|
|
169
|
+
const finalTemplateType = artifact.isYamlWorkflow ? 'default-workflow-yaml' : 'default-workflow';
|
|
170
|
+
// workflowTemplateType already contains full name (e.g., 'gemini-workflow-yaml'), so pass empty artifactType
|
|
171
|
+
const { content: template, extension } = await this.loadTemplate(workflowTemplateType, '', config, finalTemplateType);
|
|
172
|
+
const content = this.renderTemplate(template, artifact);
|
|
173
|
+
const filename = this.generateFilename(artifact, 'workflow', extension);
|
|
174
|
+
const filePath = path.join(targetPath, filename);
|
|
175
|
+
await this.writeFile(filePath, content);
|
|
176
|
+
count++;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return count;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Load template based on type and configuration
|
|
185
|
+
* @param {string} templateType - Template type (claude, windsurf, etc.)
|
|
186
|
+
* @param {string} artifactType - Artifact type (agent, workflow, task, tool)
|
|
187
|
+
* @param {Object} config - Installation configuration
|
|
188
|
+
* @param {string} fallbackTemplateType - Fallback template type if requested template not found
|
|
189
|
+
* @returns {Promise<{content: string, extension: string}>} Template content and extension
|
|
190
|
+
*/
|
|
191
|
+
async loadTemplate(templateType, artifactType, config = {}, fallbackTemplateType = null) {
|
|
192
|
+
const { header_template, body_template } = config;
|
|
193
|
+
|
|
194
|
+
// Check for separate header/body templates
|
|
195
|
+
if (header_template || body_template) {
|
|
196
|
+
const content = await this.loadSplitTemplates(templateType, artifactType, header_template, body_template);
|
|
197
|
+
// Allow config to override extension, default to .md
|
|
198
|
+
const ext = config.extension || '.md';
|
|
199
|
+
const normalizedExt = ext.startsWith('.') ? ext : `.${ext}`;
|
|
200
|
+
return { content, extension: normalizedExt };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Load combined template - try multiple extensions
|
|
204
|
+
// If artifactType is empty, templateType already contains full name (e.g., 'gemini-workflow-yaml')
|
|
205
|
+
const templateBaseName = artifactType ? `${templateType}-${artifactType}` : templateType;
|
|
206
|
+
const templateDir = path.join(__dirname, 'templates', 'combined');
|
|
207
|
+
const extensions = ['.md', '.toml', '.yaml', '.yml'];
|
|
208
|
+
|
|
209
|
+
for (const ext of extensions) {
|
|
210
|
+
const templatePath = path.join(templateDir, templateBaseName + ext);
|
|
211
|
+
if (await fs.pathExists(templatePath)) {
|
|
212
|
+
const content = await fs.readFile(templatePath, 'utf8');
|
|
213
|
+
return { content, extension: ext };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Fall back to default template (if provided)
|
|
218
|
+
if (fallbackTemplateType) {
|
|
219
|
+
for (const ext of extensions) {
|
|
220
|
+
const fallbackPath = path.join(templateDir, `${fallbackTemplateType}${ext}`);
|
|
221
|
+
if (await fs.pathExists(fallbackPath)) {
|
|
222
|
+
const content = await fs.readFile(fallbackPath, 'utf8');
|
|
223
|
+
return { content, extension: ext };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Ultimate fallback - minimal template
|
|
229
|
+
return { content: this.getDefaultTemplate(artifactType), extension: '.md' };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Load split templates (header + body)
|
|
234
|
+
* @param {string} templateType - Template type
|
|
235
|
+
* @param {string} artifactType - Artifact type
|
|
236
|
+
* @param {string} headerTpl - Header template name
|
|
237
|
+
* @param {string} bodyTpl - Body template name
|
|
238
|
+
* @returns {Promise<string>} Combined template content
|
|
239
|
+
*/
|
|
240
|
+
async loadSplitTemplates(templateType, artifactType, headerTpl, bodyTpl) {
|
|
241
|
+
let header = '';
|
|
242
|
+
let body = '';
|
|
243
|
+
|
|
244
|
+
// Load header template
|
|
245
|
+
if (headerTpl) {
|
|
246
|
+
const headerPath = path.join(__dirname, 'templates', 'split', headerTpl);
|
|
247
|
+
if (await fs.pathExists(headerPath)) {
|
|
248
|
+
header = await fs.readFile(headerPath, 'utf8');
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
// Use default header for template type
|
|
252
|
+
const defaultHeaderPath = path.join(__dirname, 'templates', 'split', templateType, 'header.md');
|
|
253
|
+
if (await fs.pathExists(defaultHeaderPath)) {
|
|
254
|
+
header = await fs.readFile(defaultHeaderPath, 'utf8');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Load body template
|
|
259
|
+
if (bodyTpl) {
|
|
260
|
+
const bodyPath = path.join(__dirname, 'templates', 'split', bodyTpl);
|
|
261
|
+
if (await fs.pathExists(bodyPath)) {
|
|
262
|
+
body = await fs.readFile(bodyPath, 'utf8');
|
|
263
|
+
}
|
|
264
|
+
} else {
|
|
265
|
+
// Use default body for template type
|
|
266
|
+
const defaultBodyPath = path.join(__dirname, 'templates', 'split', templateType, 'body.md');
|
|
267
|
+
if (await fs.pathExists(defaultBodyPath)) {
|
|
268
|
+
body = await fs.readFile(defaultBodyPath, 'utf8');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Combine header and body
|
|
273
|
+
return `${header}\n${body}`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Get default minimal template
|
|
278
|
+
* @param {string} artifactType - Artifact type
|
|
279
|
+
* @returns {string} Default template
|
|
280
|
+
*/
|
|
281
|
+
getDefaultTemplate(artifactType) {
|
|
282
|
+
if (artifactType === 'agent') {
|
|
283
|
+
return `---
|
|
284
|
+
name: '{{name}}'
|
|
285
|
+
description: '{{description}}'
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
You must fully embody this agent's persona and follow all activation instructions exactly as specified.
|
|
289
|
+
|
|
290
|
+
<agent-activation CRITICAL="TRUE">
|
|
291
|
+
1. LOAD the FULL agent file from {project-root}/{{bmadFolderName}}/{{path}}
|
|
292
|
+
2. READ its entire contents - this contains the complete agent persona, menu, and instructions
|
|
293
|
+
3. FOLLOW every step in the <activation> section precisely
|
|
294
|
+
</agent-activation>
|
|
295
|
+
`;
|
|
296
|
+
}
|
|
297
|
+
return `---
|
|
298
|
+
name: '{{name}}'
|
|
299
|
+
description: '{{description}}'
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
# {{name}}
|
|
303
|
+
|
|
304
|
+
LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
305
|
+
`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Render template with artifact data
|
|
310
|
+
* @param {string} template - Template content
|
|
311
|
+
* @param {Object} artifact - Artifact data
|
|
312
|
+
* @returns {string} Rendered content
|
|
313
|
+
*/
|
|
314
|
+
renderTemplate(template, artifact) {
|
|
315
|
+
// Use the appropriate path property based on artifact type
|
|
316
|
+
let pathToUse = artifact.relativePath || '';
|
|
317
|
+
if (artifact.type === 'agent-launcher') {
|
|
318
|
+
pathToUse = artifact.agentPath || artifact.relativePath || '';
|
|
319
|
+
} else if (artifact.type === 'workflow-command') {
|
|
320
|
+
pathToUse = artifact.workflowPath || artifact.relativePath || '';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
let rendered = template
|
|
324
|
+
.replaceAll('{{name}}', artifact.name || '')
|
|
325
|
+
.replaceAll('{{module}}', artifact.module || 'core')
|
|
326
|
+
.replaceAll('{{path}}', pathToUse)
|
|
327
|
+
.replaceAll('{{description}}', artifact.description || `${artifact.name} ${artifact.type || ''}`)
|
|
328
|
+
.replaceAll('{{workflow_path}}', pathToUse);
|
|
329
|
+
|
|
330
|
+
// Replace _bmad placeholder with actual folder name
|
|
331
|
+
rendered = rendered.replaceAll('_bmad', this.bmadFolderName);
|
|
332
|
+
|
|
333
|
+
// Replace {{bmadFolderName}} placeholder if present
|
|
334
|
+
rendered = rendered.replaceAll('{{bmadFolderName}}', this.bmadFolderName);
|
|
335
|
+
|
|
336
|
+
return rendered;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Generate filename for artifact
|
|
341
|
+
* @param {Object} artifact - Artifact data
|
|
342
|
+
* @param {string} artifactType - Artifact type (agent, workflow, task, tool)
|
|
343
|
+
* @param {string} extension - File extension to use (e.g., '.md', '.toml')
|
|
344
|
+
* @returns {string} Generated filename
|
|
345
|
+
*/
|
|
346
|
+
generateFilename(artifact, artifactType, extension = '.md') {
|
|
347
|
+
const { toDashPath } = require('./shared/path-utils');
|
|
348
|
+
|
|
349
|
+
// Reuse central logic to ensure consistent naming conventions
|
|
350
|
+
const standardName = toDashPath(artifact.relativePath);
|
|
351
|
+
|
|
352
|
+
// Clean up potential double extensions from source files (e.g. .yaml.md -> .md)
|
|
353
|
+
const baseName = standardName.replace(/\.(yaml|yml)\.md$/, '.md');
|
|
354
|
+
|
|
355
|
+
// If using default markdown, preserve the bmad-agent- prefix for agents
|
|
356
|
+
if (extension === '.md') {
|
|
357
|
+
return baseName;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// For other extensions (e.g., .toml), replace .md extension
|
|
361
|
+
// Note: agent prefix is preserved even with non-markdown extensions
|
|
362
|
+
return baseName.replace(/\.md$/, extension);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Print installation summary
|
|
367
|
+
* @param {Object} results - Installation results
|
|
368
|
+
* @param {string} targetDir - Target directory (relative)
|
|
369
|
+
*/
|
|
370
|
+
printSummary(results, targetDir) {
|
|
371
|
+
console.log(chalk.green(`\n✓ ${this.name} configured:`));
|
|
372
|
+
if (results.agents > 0) {
|
|
373
|
+
console.log(chalk.dim(` - ${results.agents} agents installed`));
|
|
374
|
+
}
|
|
375
|
+
if (results.workflows > 0) {
|
|
376
|
+
console.log(chalk.dim(` - ${results.workflows} workflow commands generated`));
|
|
377
|
+
}
|
|
378
|
+
if (results.tasks > 0 || results.tools > 0) {
|
|
379
|
+
console.log(chalk.dim(` - ${results.tasks + results.tools} task/tool commands generated`));
|
|
380
|
+
}
|
|
381
|
+
console.log(chalk.dim(` - Destination: ${targetDir}`));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Cleanup IDE configuration
|
|
386
|
+
* @param {string} projectDir - Project directory
|
|
387
|
+
*/
|
|
388
|
+
async cleanup(projectDir) {
|
|
389
|
+
// Clean all target directories
|
|
390
|
+
if (this.installerConfig?.targets) {
|
|
391
|
+
for (const target of this.installerConfig.targets) {
|
|
392
|
+
await this.cleanupTarget(projectDir, target.target_dir);
|
|
393
|
+
}
|
|
394
|
+
} else if (this.installerConfig?.target_dir) {
|
|
395
|
+
await this.cleanupTarget(projectDir, this.installerConfig.target_dir);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Cleanup a specific target directory
|
|
401
|
+
* @param {string} projectDir - Project directory
|
|
402
|
+
* @param {string} targetDir - Target directory to clean
|
|
403
|
+
*/
|
|
404
|
+
async cleanupTarget(projectDir, targetDir) {
|
|
405
|
+
const targetPath = path.join(projectDir, targetDir);
|
|
406
|
+
|
|
407
|
+
if (!(await fs.pathExists(targetPath))) {
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Remove all bmad* files
|
|
412
|
+
let entries;
|
|
413
|
+
try {
|
|
414
|
+
entries = await fs.readdir(targetPath);
|
|
415
|
+
} catch {
|
|
416
|
+
// Directory exists but can't be read - skip cleanup
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (!entries || !Array.isArray(entries)) {
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
let removedCount = 0;
|
|
425
|
+
|
|
426
|
+
for (const entry of entries) {
|
|
427
|
+
// Skip non-strings or undefined entries
|
|
428
|
+
if (!entry || typeof entry !== 'string') {
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
if (entry.startsWith('bmad')) {
|
|
432
|
+
const entryPath = path.join(targetPath, entry);
|
|
433
|
+
const stat = await fs.stat(entryPath);
|
|
434
|
+
if (stat.isFile()) {
|
|
435
|
+
await fs.remove(entryPath);
|
|
436
|
+
removedCount++;
|
|
437
|
+
} else if (stat.isDirectory()) {
|
|
438
|
+
await fs.remove(entryPath);
|
|
439
|
+
removedCount++;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (removedCount > 0) {
|
|
445
|
+
console.log(chalk.dim(` Cleaned ${removedCount} BMAD files from ${targetDir}`));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
module.exports = { ConfigDrivenIdeSetup };
|