bmad-plus 0.12.2 → 0.14.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/CHANGELOG.md +47 -0
- package/README.md +96 -461
- package/package.json +12 -5
- package/readme-international/README.de.md +42 -9
- package/readme-international/README.es.md +43 -10
- package/readme-international/README.fr.md +42 -9
- package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +5 -3
- package/src/bmad-plus/module.yaml +270 -220
- package/src/bmad-plus/packs/pack-dev-studio/README.md +133 -141
- package/src/bmad-plus/packs/pack-dev-studio/SKILL.md +49 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +35 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +59 -59
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +55 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +58 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +55 -132
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +63 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +54 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +54 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +32 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +67 -71
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +61 -90
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +56 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +55 -88
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +20 -153
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +20 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +14 -220
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +20 -329
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +15 -314
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +15 -355
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +15 -375
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +14 -357
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +13 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +52 -65
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +14 -81
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +14 -31
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +14 -45
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +13 -128
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +61 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +55 -298
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +52 -426
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +48 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +22 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +60 -482
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +50 -184
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +57 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +56 -108
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +54 -1509
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +54 -296
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +41 -283
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +58 -18
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +103 -72
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +55 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +34 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +46 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/steps/step-01-init.md +10 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +30 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +57 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +47 -138
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +48 -34
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +51 -68
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +46 -3
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +68 -109
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +53 -174
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +39 -53
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +45 -83
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +45 -176
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +45 -63
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +53 -124
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +44 -100
- package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +56 -116
- package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +49 -12
- package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +51 -328
- package/src/bmad-plus/packs/pack-dev-studio/shared/catalog.json +489 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/execution.md +69 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +39 -32
- package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +85 -14
- package/src/bmad-plus/packs/pack-memory/README.md +6 -0
- package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +12 -5
- package/src/bmad-plus/packs/pack-seo/SKILL.md +27 -1
- package/src/bmad-plus/packs/pack-seo/scripts/seo_apis.py +8 -8
- package/src/bmad-plus/packs/pack-seo/scripts/seo_fetch.py +1 -2
- package/src/bmad-plus/packs/pack-seo/scripts/seo_report.py +0 -1
- package/src/bmad-plus/packs/pack-seo/seo-chief.md +16 -1
- package/src/bmad-plus/packs/pack-seo/seo-judge.md +12 -0
- package/src/bmad-plus/packs/pack-seo/seo-scout.md +12 -0
- package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +4 -4
- package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +5 -3
- package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
- package/tools/bmad-plus-npx.js +4 -2
- package/tools/build/adapters.config.js +60 -51
- package/tools/build/check-counts.js +52 -54
- package/tools/build/check-install-contract.js +450 -0
- package/tools/build/generate-adapters.js +252 -56
- package/tools/build/generate.js +364 -14
- package/tools/build/generated-adapters/.codex/AGENTS.md +20 -7
- package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +20 -7
- package/tools/build/generated-adapters/.opencode/AGENTS.md +20 -7
- package/tools/build/generated-adapters/AGENTS.md +20 -7
- package/tools/build/generated-adapters/CLAUDE.md +20 -7
- package/tools/build/generated-adapters/CONVENTIONS.md +20 -7
- package/tools/build/generated-adapters/GEMINI.md +20 -7
- package/tools/build/module.template.yaml +82 -0
- package/tools/build/pack-delivery.js +78 -0
- package/tools/cli/bmad-plus-cli.js +16 -1
- package/tools/cli/commands/doctor.js +49 -217
- package/tools/cli/commands/install.js +109 -164
- package/tools/cli/commands/studio.js +68 -0
- package/tools/cli/commands/uninstall.js +173 -65
- package/tools/cli/commands/update-check.js +31 -0
- package/tools/cli/commands/update-policy.js +39 -0
- package/tools/cli/commands/update.js +102 -113
- package/tools/cli/i18n.js +60 -0
- package/tools/cli/lib/ide-config.js +4 -261
- package/tools/cli/lib/install-manifest.js +17 -0
- package/tools/cli/lib/installation-health.js +360 -0
- package/tools/cli/lib/installed-adapters.js +89 -0
- package/tools/cli/lib/npm-runner.js +177 -0
- package/tools/cli/lib/pack-copy.js +62 -66
- package/tools/cli/lib/packs.js +546 -3
- package/tools/cli/lib/python-health.js +233 -0
- package/tools/cli/lib/python-provision.js +2 -2
- package/tools/cli/lib/studio.js +310 -0
- package/tools/cli/lib/update-check.js +153 -0
- package/tools/cli/lib/update-dispatch.js +182 -0
- package/tools/cli/lib/update-policy.js +90 -0
- package/tools/cli/lib/update-transaction.js +334 -0
- package/tools/release/publication-content.js +901 -0
|
@@ -1,233 +1,65 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BMAD+ Doctor Command
|
|
3
|
-
* Checks installation integrity and reports issues
|
|
4
|
-
*
|
|
5
|
-
* Author: Laurent Rochetta
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const path = require('node:path');
|
|
9
|
-
const fs = require('node:fs');
|
|
1
|
+
/** Human and JSON output share one report; runtime execution is explicitly opt-in. */
|
|
10
2
|
const clack = require('@clack/prompts');
|
|
11
3
|
const pc = require('picocolors');
|
|
12
|
-
const {
|
|
13
|
-
const { PACKS, EXPECTED_AGENTS } = require('../lib/packs');
|
|
4
|
+
const { collectInstallationHealth } = require('../lib/installation-health');
|
|
14
5
|
|
|
15
6
|
module.exports = {
|
|
16
7
|
command: 'doctor',
|
|
17
|
-
description: 'Check BMAD+ installation integrity',
|
|
8
|
+
description: 'Check BMAD+ installation integrity and declared capabilities',
|
|
18
9
|
options: [
|
|
19
10
|
['-d, --directory <path>', 'Project directory (default: current directory)'],
|
|
20
11
|
['-l, --lang <code>', 'Language code: en, fr, es, de, pt-br, ru, zh, he, ja, it'],
|
|
12
|
+
['--json', 'Print the diagnostic report as JSON (read-only unless a probe is requested)'],
|
|
13
|
+
['--verify-python', 'Execute installed Python interpreters to verify versions and imports'],
|
|
21
14
|
],
|
|
22
|
-
action: async (options) => {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
let warnings = 0;
|
|
31
|
-
let errors = 0;
|
|
32
|
-
|
|
33
|
-
// ── Check 1: Installation manifest ──
|
|
34
|
-
checks++;
|
|
35
|
-
const manifestPath = path.join(projectDir, '_bmad', '.bmad-plus-install.json');
|
|
36
|
-
if (!fs.existsSync(manifestPath)) {
|
|
37
|
-
clack.log.error('❌ BMAD+ is not installed in this directory');
|
|
38
|
-
clack.outro(pc.red('Run `npx bmad-plus install` first.'));
|
|
15
|
+
action: async (options = {}) => {
|
|
16
|
+
const report = collectInstallationHealth({
|
|
17
|
+
projectDir: options.directory,
|
|
18
|
+
verifyPython: Boolean(options.verifyPython),
|
|
19
|
+
});
|
|
20
|
+
process.exitCode = report.summary.errors ? 1 : 0;
|
|
21
|
+
if (options.json) {
|
|
22
|
+
console.log(JSON.stringify(report, null, 2));
|
|
39
23
|
return;
|
|
40
24
|
}
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
warnings++;
|
|
25
|
+
clack.intro(pc.bgBlue(pc.white(' BMAD+ Doctor v' + report.versions.cli + ' ')));
|
|
26
|
+
for (const entry of report.checks.filter(
|
|
27
|
+
(item) => item.status !== 'pass' || item.code === 'manifest.valid'
|
|
28
|
+
)) {
|
|
29
|
+
const log =
|
|
30
|
+
entry.status === 'error'
|
|
31
|
+
? clack.log.error
|
|
32
|
+
: entry.status === 'warning'
|
|
33
|
+
? clack.log.warn
|
|
34
|
+
: clack.log.success;
|
|
35
|
+
log(
|
|
36
|
+
entry.message +
|
|
37
|
+
(entry.path && !entry.message.includes(entry.path) ? ' (' + entry.path + ')' : '')
|
|
38
|
+
);
|
|
56
39
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
// Check each expected agent from manifest packs using shared EXPECTED_AGENTS module
|
|
69
|
-
for (const pack of (manifest.packs || ['core'])) {
|
|
70
|
-
const entry = EXPECTED_AGENTS[pack];
|
|
71
|
-
if (!entry) continue;
|
|
72
|
-
|
|
73
|
-
// Check individual agent directories
|
|
74
|
-
for (const agent of (entry.agents || [])) {
|
|
75
|
-
checks++;
|
|
76
|
-
const agentPath = path.join(agentsDir, agent);
|
|
77
|
-
if (fs.existsSync(agentPath)) {
|
|
78
|
-
passed++;
|
|
79
|
-
} else {
|
|
80
|
-
clack.log.warn(`⚠️ Missing agent: ${agent} (pack: ${pack})`);
|
|
81
|
-
warnings++;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Check pack directory if applicable
|
|
86
|
-
if (entry.packDir) {
|
|
87
|
-
checks++;
|
|
88
|
-
const packPath = path.join(agentsDir, entry.packDir);
|
|
89
|
-
if (fs.existsSync(packPath)) {
|
|
90
|
-
passed++;
|
|
91
|
-
|
|
92
|
-
// These packs bundle their agents as files inside packDir — verify
|
|
93
|
-
// them there (not as loose directories, which would falsely warn).
|
|
94
|
-
for (const agentFile of (entry.packAgents || [])) {
|
|
95
|
-
checks++;
|
|
96
|
-
if (fs.existsSync(path.join(packPath, agentFile))) {
|
|
97
|
-
passed++;
|
|
98
|
-
} else {
|
|
99
|
-
clack.log.warn(`⚠️ Missing agent: ${agentFile} (pack: ${pack})`);
|
|
100
|
-
warnings++;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
clack.log.warn(`⚠️ Missing pack directory: ${entry.packDir} (pack: ${pack})`);
|
|
105
|
-
warnings++;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
clack.log.error('❌ No .agents/skills/ directory found');
|
|
111
|
-
errors++;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ── Check 4: Config files ──
|
|
115
|
-
checks++;
|
|
116
|
-
const configPath = path.join(projectDir, '_bmad', 'config.yaml');
|
|
117
|
-
if (fs.existsSync(configPath)) {
|
|
118
|
-
clack.log.success('✅ config.yaml present');
|
|
119
|
-
passed++;
|
|
120
|
-
} else {
|
|
121
|
-
clack.log.error('❌ config.yaml missing');
|
|
122
|
-
errors++;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// ── Check 5: Module config ──
|
|
126
|
-
checks++;
|
|
127
|
-
const moduleYaml = path.join(projectDir, '_bmad', 'module.yaml');
|
|
128
|
-
if (fs.existsSync(moduleYaml)) {
|
|
129
|
-
clack.log.success('✅ module.yaml present');
|
|
130
|
-
passed++;
|
|
131
|
-
} else {
|
|
132
|
-
clack.log.warn('⚠️ module.yaml missing');
|
|
133
|
-
warnings++;
|
|
40
|
+
if (report.versions.installed) {
|
|
41
|
+
clack.note(
|
|
42
|
+
[
|
|
43
|
+
report.summary.passed + '/' + report.checks.length + ' checks passed',
|
|
44
|
+
report.summary.errors + ' error(s), ' + report.summary.warnings + ' warning(s)',
|
|
45
|
+
'Installed: v' + report.versions.installed + ' | Executing CLI: v' + report.versions.cli,
|
|
46
|
+
'Ownership inventory: ' + report.inventory.status,
|
|
47
|
+
'Integrations use host-managed execution; host capabilities were not probed.',
|
|
48
|
+
].join('\n'),
|
|
49
|
+
'BMAD+ Health Report'
|
|
50
|
+
);
|
|
134
51
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
52
|
+
clack.log.info(
|
|
53
|
+
'Published version is not checked here. Use `bmad-plus update-check --json` to check npm.'
|
|
54
|
+
);
|
|
55
|
+
clack.outro(
|
|
56
|
+
report.status === 'ok'
|
|
57
|
+
? pc.green('Installation checks passed.')
|
|
58
|
+
: pc.yellow(
|
|
59
|
+
options.verifyPython
|
|
60
|
+
? 'Review the reported evidence. Python import probes ran; no provisioning was requested.'
|
|
61
|
+
: 'Review the reported evidence before changing files. Doctor did not modify the project.'
|
|
62
|
+
)
|
|
140
63
|
);
|
|
141
|
-
if (ideFiles.length > 0) {
|
|
142
|
-
clack.log.success(`✅ ${ideFiles.length} IDE config(s): ${ideFiles.join(', ')}`);
|
|
143
|
-
passed++;
|
|
144
|
-
} else {
|
|
145
|
-
clack.log.warn('⚠️ No IDE config files found');
|
|
146
|
-
warnings++;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// ── Check 7: Output directories ──
|
|
150
|
-
checks++;
|
|
151
|
-
const outputDir = path.join(projectDir, '_bmad-output');
|
|
152
|
-
if (fs.existsSync(outputDir)) {
|
|
153
|
-
clack.log.success('✅ _bmad-output/ directory exists');
|
|
154
|
-
passed++;
|
|
155
|
-
} else {
|
|
156
|
-
clack.log.warn('⚠️ _bmad-output/ not found');
|
|
157
|
-
warnings++;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ── Check 8: Skills integrity (SKILL.md exists in pack dirs) ──
|
|
161
|
-
if (manifest.packs && manifest.packs.includes('seo')) {
|
|
162
|
-
checks++;
|
|
163
|
-
const seoSkill = path.join(agentsDir, 'pack-seo', 'SKILL.md');
|
|
164
|
-
if (fs.existsSync(seoSkill)) {
|
|
165
|
-
clack.log.success('✅ SEO Engine SKILL.md present');
|
|
166
|
-
passed++;
|
|
167
|
-
} else {
|
|
168
|
-
clack.log.error('❌ SEO Engine SKILL.md missing — /seo audit will not work');
|
|
169
|
-
errors++;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// ── Check 9: PACKS ↔ module.yaml sync ──
|
|
174
|
-
checks++;
|
|
175
|
-
try {
|
|
176
|
-
const yaml = require('js-yaml');
|
|
177
|
-
const moduleYamlSrc = path.join(__dirname, '..', '..', '..', 'src', 'bmad-plus', 'module.yaml');
|
|
178
|
-
if (fs.existsSync(moduleYamlSrc)) {
|
|
179
|
-
const moduleContent = yaml.load(fs.readFileSync(moduleYamlSrc, 'utf8'));
|
|
180
|
-
const modulePackIds = Object.keys(moduleContent.packs || {});
|
|
181
|
-
|
|
182
|
-
// Use shared PACKS module instead of fragile regex on install.js source
|
|
183
|
-
const cliPackIds = Object.keys(PACKS);
|
|
184
|
-
|
|
185
|
-
// Find mismatches
|
|
186
|
-
const missingInCLI = modulePackIds.filter(p => !cliPackIds.includes(p));
|
|
187
|
-
const missingInModule = cliPackIds.filter(p => !modulePackIds.includes(p));
|
|
188
|
-
|
|
189
|
-
if (missingInCLI.length === 0 && missingInModule.length === 0) {
|
|
190
|
-
clack.log.success(`✅ PACKS ↔ module.yaml in sync (${modulePackIds.length} packs)`);
|
|
191
|
-
passed++;
|
|
192
|
-
} else {
|
|
193
|
-
if (missingInCLI.length > 0) {
|
|
194
|
-
clack.log.warn(`⚠️ Packs in module.yaml but missing from CLI PACKS: ${missingInCLI.join(', ')}`);
|
|
195
|
-
}
|
|
196
|
-
if (missingInModule.length > 0) {
|
|
197
|
-
clack.log.warn(`⚠️ Packs in CLI PACKS but missing from module.yaml: ${missingInModule.join(', ')}`);
|
|
198
|
-
}
|
|
199
|
-
warnings++;
|
|
200
|
-
}
|
|
201
|
-
} else {
|
|
202
|
-
clack.log.info(pc.dim('ℹ️ PACKS↔module.yaml check skipped (source not available in npx context)'));
|
|
203
|
-
passed++; // Not a failure — just unavailable
|
|
204
|
-
}
|
|
205
|
-
} catch (e) {
|
|
206
|
-
clack.log.info(pc.dim(`ℹ️ PACKS↔module.yaml check skipped: ${e.message}`));
|
|
207
|
-
passed++; // Graceful skip
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// ── Summary ──
|
|
211
|
-
const summaryColor = errors > 0 ? pc.red : warnings > 0 ? pc.yellow : pc.green;
|
|
212
|
-
const summaryIcon = errors > 0 ? '❌' : warnings > 0 ? '⚠️' : '✅';
|
|
213
|
-
|
|
214
|
-
clack.note([
|
|
215
|
-
`${summaryIcon} ${passed}/${checks} checks passed`,
|
|
216
|
-
errors > 0 ? `❌ ${errors} error(s)` : null,
|
|
217
|
-
warnings > 0 ? `⚠️ ${warnings} warning(s)` : null,
|
|
218
|
-
'',
|
|
219
|
-
`📦 Version: v${manifest.version}`,
|
|
220
|
-
`📅 Installed: ${manifest.installed.split('T')[0]}`,
|
|
221
|
-
`🗣️ Language: ${manifest.language || 'N/A'}`,
|
|
222
|
-
`📦 Packs: ${(manifest.packs || ['core']).join(', ')}`,
|
|
223
|
-
].filter(Boolean).join('\n'), '🩺 BMAD+ Health Report');
|
|
224
|
-
|
|
225
|
-
clack.outro(summaryColor(
|
|
226
|
-
errors > 0
|
|
227
|
-
? 'Issues found — run `npx bmad-plus install` to fix'
|
|
228
|
-
: warnings > 0
|
|
229
|
-
? 'Minor issues found — consider running `npx bmad-plus update`'
|
|
230
|
-
: 'Everything looks great! 🎉'
|
|
231
|
-
));
|
|
232
64
|
},
|
|
233
65
|
};
|
|
@@ -12,59 +12,18 @@ const fsExtra = require('fs-extra');
|
|
|
12
12
|
const clack = require('@clack/prompts');
|
|
13
13
|
const pc = require('picocolors');
|
|
14
14
|
const { t, getLanguageOptions, getCommLanguageOptions } = require('../i18n');
|
|
15
|
-
const { PACKS } = require('../lib/packs');
|
|
16
|
-
const { copyPackFiles } = require('../lib/pack-copy');
|
|
15
|
+
const { PACKS, DERIVED } = require('../lib/packs');
|
|
16
|
+
const { copyPackFiles, copyManagedFile } = require('../lib/pack-copy');
|
|
17
|
+
const { acquireProjectLock, inventoryOf, collectManagedFiles } = require('../lib/update-transaction');
|
|
17
18
|
const { initMemory } = require('../lib/memory-init');
|
|
18
|
-
const {
|
|
19
|
+
const { generateUserFiles, IDE_CONFIGS } = require('../../build/generate-adapters');
|
|
20
|
+
const { writeIDEConfigs, safeAdapterPath } = require('../lib/installed-adapters');
|
|
21
|
+
const { readInstallManifest } = require('../lib/install-manifest');
|
|
22
|
+
const yaml = require('js-yaml');
|
|
19
23
|
const { validateUserName } = require('../lib/validate');
|
|
20
24
|
const { provisionPack } = require('../lib/python-provision');
|
|
21
25
|
|
|
22
|
-
|
|
23
|
-
// Provisioned only when the user opts in with --provision-python, so the default
|
|
24
|
-
// install (and the test suite) never spawns Python. Kills SEO "broken on arrival".
|
|
25
|
-
// Paths are resolved relative to the PACKAGE root (projectRoot below), so they
|
|
26
|
-
// must live under a directory shipped in package.json "files". The SEO toolkit
|
|
27
|
-
// is embarked in-pack (src/bmad-plus/packs/pack-seo) precisely so npm consumers
|
|
28
|
-
// receive the scripts + requirements the SKILL.md commands them to run.
|
|
29
|
-
const PYTHON_PACKS = {
|
|
30
|
-
seo: {
|
|
31
|
-
requirements: ['src', 'bmad-plus', 'packs', 'pack-seo', 'requirements.txt'],
|
|
32
|
-
verifyModules: ['requests', 'bs4', 'defusedxml', 'lxml'],
|
|
33
|
-
},
|
|
34
|
-
memory: {
|
|
35
|
-
// NOTE: mcp-server is not in package.json "files" → this still warn-skips for
|
|
36
|
-
// npm consumers (memory RAG provisioning is a separate, pre-existing gap).
|
|
37
|
-
requirements: ['mcp-server', 'requirements.txt'],
|
|
38
|
-
verifyModules: ['chromadb'],
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// Pack definitions are imported from the shared module: require('../lib/packs').PACKS
|
|
43
|
-
|
|
44
|
-
// ── Root-config ownership markers (PROD-01) ──
|
|
45
|
-
//
|
|
46
|
-
// install writes CLAUDE.md / GEMINI.md / AGENTS.md, but TWO producers own those
|
|
47
|
-
// same paths, each stamping its own marker:
|
|
48
|
-
// - lib/ide-config.js → INSTALLER_MARKER (this installer's template)
|
|
49
|
-
// - tools/build/generate-adapters.js → GENERATED_MARKER (registry-generated spine)
|
|
50
|
-
// Recognizing only the first is what let `install --yes` clobber a generated spine.
|
|
51
|
-
const INSTALLER_MARKER = 'BMAD+ — AI Agent Configuration';
|
|
52
|
-
const GENERATOR_MARKER_FALLBACK = 'AUTO-GENERATED by tools/build/generate-adapters.js';
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* The marker the adapter generator stamps on every root file it owns.
|
|
56
|
-
* Read from the generator's own export so the two cannot drift apart — a
|
|
57
|
-
* hand-typed copy is how INSTALLER_MARKER drifted from the spine in the first
|
|
58
|
-
* place. The fallback only covers trees published without the build tooling;
|
|
59
|
-
* tests/unit/install-spine.test.js pins both values to their real producer.
|
|
60
|
-
*/
|
|
61
|
-
function resolveGeneratorMarker() {
|
|
62
|
-
try {
|
|
63
|
-
return require('../../build/generate-adapters').GENERATED_MARKER || GENERATOR_MARKER_FALLBACK;
|
|
64
|
-
} catch {
|
|
65
|
-
return GENERATOR_MARKER_FALLBACK;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
26
|
+
const PYTHON_PACKS = DERIVED.pythonPacks;
|
|
68
27
|
|
|
69
28
|
module.exports = {
|
|
70
29
|
command: 'install',
|
|
@@ -74,12 +33,34 @@ module.exports = {
|
|
|
74
33
|
['-p, --packs <packs>', 'Comma-separated pack IDs: core,osint,all (default: interactive)'],
|
|
75
34
|
['-y, --yes', 'Accept all defaults, skip prompts'],
|
|
76
35
|
['-l, --lang <code>', 'Language code: en, fr, es, de, pt-br, ru, zh, he, ja, it (overrides auto-detection)'],
|
|
77
|
-
['--tools <tools>', 'Comma-separated IDE IDs (default: auto-detect)'],
|
|
78
|
-
['--
|
|
36
|
+
['--tools <tools>', 'Comma-separated IDE IDs, all, or none (default: auto-detect)'],
|
|
37
|
+
['--mode <mode>', 'Execution mode: manual, autopilot, hybrid (default: manual)'],
|
|
38
|
+
['--provision-python', 'Provision isolated environments for selected Python packs'],
|
|
79
39
|
],
|
|
80
40
|
action: async (options) => {
|
|
41
|
+
// A closed/piped stdin cannot answer prompts. Require explicit defaults
|
|
42
|
+
// before creating files, instead of silently exiting halfway through UI.
|
|
43
|
+
if (!options.yes && !process.stdin.isTTY) {
|
|
44
|
+
const i = t(options.lang || 'en');
|
|
45
|
+
clack.log.error(i.noninteractive_requires_yes('install'));
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
81
50
|
const projectDir = path.resolve(options.directory || process.cwd());
|
|
82
51
|
const bmadSrc = path.join(__dirname, '..', '..', '..', 'src', 'bmad-plus');
|
|
52
|
+
const previousManifestPath = safeAdapterPath(projectDir, '_bmad/.bmad-plus-install.json');
|
|
53
|
+
const previousManifestBytes = fs.existsSync(previousManifestPath) ? fs.readFileSync(previousManifestPath) : null;
|
|
54
|
+
let previousManifest = null;
|
|
55
|
+
let manifestError;
|
|
56
|
+
if (fs.existsSync(previousManifestPath)) {
|
|
57
|
+
try {
|
|
58
|
+
previousManifest = readInstallManifest(previousManifestPath);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
manifestError = error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (options.mode && !['manual', 'autopilot', 'hybrid'].includes(options.mode)) throw new Error('Unknown execution mode: ' + options.mode);
|
|
83
64
|
|
|
84
65
|
// ── Step 0: Language Selection ──
|
|
85
66
|
const pkgJson = require('../../../package.json');
|
|
@@ -151,6 +132,8 @@ module.exports = {
|
|
|
151
132
|
if (options.tools) {
|
|
152
133
|
if (options.tools === 'none' || options.tools === 'skip') {
|
|
153
134
|
detectedIDEs = [];
|
|
135
|
+
} else if (options.tools === 'all') {
|
|
136
|
+
detectedIDEs = Object.keys(IDE_CONFIGS);
|
|
154
137
|
} else {
|
|
155
138
|
detectedIDEs = options.tools.split(',').map(t => t.trim()).filter(t => IDE_CONFIGS[t]);
|
|
156
139
|
}
|
|
@@ -196,6 +179,7 @@ module.exports = {
|
|
|
196
179
|
// ── Step 3: User Config ──
|
|
197
180
|
let userName = process.env.USER || process.env.USERNAME || 'Developer';
|
|
198
181
|
let commLang = 'French';
|
|
182
|
+
let execMode = options.mode || 'manual';
|
|
199
183
|
|
|
200
184
|
if (!options.yes) {
|
|
201
185
|
const userConfig = await clack.group({
|
|
@@ -230,6 +214,37 @@ module.exports = {
|
|
|
230
214
|
for (const w of warnings) clack.log.warn(w);
|
|
231
215
|
userName = validatedName;
|
|
232
216
|
commLang = userConfig.commLang;
|
|
217
|
+
execMode = options.mode || userConfig.execMode;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const releaseLock = acquireProjectLock(projectDir);
|
|
221
|
+
try {
|
|
222
|
+
const { safeTarget } = require('../../build/generate-adapters');
|
|
223
|
+
const plannedFiles = collectManagedFiles({ projectDir, packs: selectedPacks });
|
|
224
|
+
if (detectedIDEs.length) plannedFiles.push(...generateUserFiles(DERIVED, {
|
|
225
|
+
packs: selectedPacks, tools: detectedIDEs, userName, language: commLang,
|
|
226
|
+
}));
|
|
227
|
+
for (const { file } of plannedFiles) safeTarget(projectDir, file);
|
|
228
|
+
safeTarget(projectDir, '_bmad/config.yaml');
|
|
229
|
+
safeTarget(projectDir, '_bmad/.bmad-plus-install.json');
|
|
230
|
+
for (const directory of ['.agents/skills', '.agents/data', '_bmad', '_bmad-output/discovery', '_bmad-output/build', 'docs']) {
|
|
231
|
+
safeTarget(projectDir, directory + '/.bmad-preflight');
|
|
232
|
+
}
|
|
233
|
+
const currentManifestBytes = fs.existsSync(previousManifestPath) ? fs.readFileSync(previousManifestPath) : null;
|
|
234
|
+
if (previousManifestBytes === null ? currentManifestBytes !== null :
|
|
235
|
+
currentManifestBytes === null || !previousManifestBytes.equals(currentManifestBytes)) {
|
|
236
|
+
throw new Error('Installation changed while preparing install. Please retry.');
|
|
237
|
+
}
|
|
238
|
+
// Preserve the unreadable manifest before repair; do not trust its ownership
|
|
239
|
+
// hashes. The adapter writer will retain locally edited user instructions.
|
|
240
|
+
if (manifestError) {
|
|
241
|
+
const backupBase = '_bmad/.bmad-plus-install.json.corrupt.bak';
|
|
242
|
+
let backupFile = backupBase;
|
|
243
|
+
let index = 1;
|
|
244
|
+
while (fs.existsSync(safeAdapterPath(projectDir, backupFile))) backupFile = `${backupBase}.${index++}`;
|
|
245
|
+
const backupPath = safeAdapterPath(projectDir, backupFile);
|
|
246
|
+
fs.copyFileSync(safeAdapterPath(projectDir, '_bmad/.bmad-plus-install.json'), backupPath, fs.constants.COPYFILE_EXCL);
|
|
247
|
+
clack.log.warn(`${i.manifest_invalid(manifestError.message)}\nBackup: ${backupPath}`);
|
|
233
248
|
}
|
|
234
249
|
|
|
235
250
|
// ── Step 4: Install Files ──
|
|
@@ -248,6 +263,13 @@ module.exports = {
|
|
|
248
263
|
let copiedAgents = 0;
|
|
249
264
|
let copiedSkills = 0;
|
|
250
265
|
let copiedFiles = 0;
|
|
266
|
+
const previousInventory = inventoryOf(previousManifest || {}) || {};
|
|
267
|
+
const fileInventory = { ...previousInventory };
|
|
268
|
+
let inventoryComplete = true;
|
|
269
|
+
const onFileConflict = file => {
|
|
270
|
+
if (!previousInventory[file]) inventoryComplete = false;
|
|
271
|
+
clack.log.warn(`${file}: locally changed or unowned content preserved.`);
|
|
272
|
+
};
|
|
251
273
|
|
|
252
274
|
const projectRoot = path.join(bmadSrc, '..', '..');
|
|
253
275
|
|
|
@@ -261,6 +283,7 @@ module.exports = {
|
|
|
261
283
|
targetDataDir,
|
|
262
284
|
projectRoot,
|
|
263
285
|
pack,
|
|
286
|
+
projectDir, inventory: fileInventory, previousInventory, onConflict: onFileConflict,
|
|
264
287
|
});
|
|
265
288
|
copiedAgents += result.copiedAgents;
|
|
266
289
|
copiedSkills += result.copiedSkills;
|
|
@@ -311,35 +334,39 @@ module.exports = {
|
|
|
311
334
|
// Copy module config
|
|
312
335
|
const moduleYaml = path.join(bmadSrc, 'module.yaml');
|
|
313
336
|
if (fs.existsSync(moduleYaml)) {
|
|
314
|
-
|
|
337
|
+
copyManagedFile({ source: moduleYaml, target: path.join(targetBmadDir, 'module.yaml'),
|
|
338
|
+
projectDir, inventory: fileInventory, previousInventory, onConflict: onFileConflict });
|
|
315
339
|
copiedFiles++;
|
|
316
340
|
}
|
|
317
341
|
|
|
318
342
|
const helpCsv = path.join(bmadSrc, 'module-help.csv');
|
|
319
343
|
if (fs.existsSync(helpCsv)) {
|
|
320
|
-
|
|
344
|
+
copyManagedFile({ source: helpCsv, target: path.join(targetBmadDir, 'module-help.csv'),
|
|
345
|
+
projectDir, inventory: fileInventory, previousInventory, onConflict: onFileConflict });
|
|
321
346
|
copiedFiles++;
|
|
322
347
|
}
|
|
323
348
|
|
|
324
349
|
spinner.stop(i.installed_summary(copiedAgents, copiedSkills, copiedFiles));
|
|
325
350
|
|
|
326
351
|
// ── Step 5: Generate IDE Configs ──
|
|
352
|
+
let adapterHashes = previousManifest?.adapterHashes || {};
|
|
327
353
|
if (detectedIDEs.length > 0) {
|
|
328
354
|
const ideSpinner = clack.spinner();
|
|
329
355
|
ideSpinner.start(i.configuring_ides);
|
|
330
356
|
|
|
331
|
-
writeIDEConfigs({
|
|
357
|
+
const outcome = writeIDEConfigs({
|
|
332
358
|
projectDir,
|
|
333
|
-
detectedIDEs,
|
|
334
|
-
|
|
359
|
+
files: generateUserFiles(DERIVED, { packs: selectedPacks, userName, language: commLang, tools: detectedIDEs }),
|
|
360
|
+
adapterHashes,
|
|
335
361
|
yes: options.yes,
|
|
336
362
|
});
|
|
363
|
+
adapterHashes = { ...adapterHashes, ...outcome.adapterHashes };
|
|
337
364
|
|
|
338
365
|
ideSpinner.stop(i.ide_configured(detectedIDEs.length));
|
|
339
366
|
}
|
|
340
367
|
|
|
341
368
|
// ── Step 6: Create config.yaml ──
|
|
342
|
-
const configYaml = generateConfigYaml(userName, commLang, projectDir);
|
|
369
|
+
const configYaml = generateConfigYaml(userName, commLang, projectDir, execMode);
|
|
343
370
|
const configPath = path.join(targetBmadDir, 'config.yaml');
|
|
344
371
|
fs.writeFileSync(configPath, configYaml, 'utf8');
|
|
345
372
|
|
|
@@ -357,6 +384,9 @@ module.exports = {
|
|
|
357
384
|
installed: new Date().toISOString(),
|
|
358
385
|
packs: selectedPacks,
|
|
359
386
|
ides: detectedIDEs,
|
|
387
|
+
adapterHashes,
|
|
388
|
+
fileInventory: { schemaVersion: 1, complete: inventoryComplete, files: fileInventory },
|
|
389
|
+
executionMode: execMode,
|
|
360
390
|
user: userName,
|
|
361
391
|
language: commLang,
|
|
362
392
|
};
|
|
@@ -484,115 +514,30 @@ module.exports = {
|
|
|
484
514
|
clack.note(agentGuide.join('\n'), i.guide_title);
|
|
485
515
|
|
|
486
516
|
clack.outro(pc.green(i.guide_ready));
|
|
517
|
+
} finally {
|
|
518
|
+
releaseLock();
|
|
519
|
+
}
|
|
487
520
|
},
|
|
488
521
|
// Exported for functional testing (not part of the public API)
|
|
489
|
-
_internal: {
|
|
490
|
-
INSTALLER_MARKER,
|
|
491
|
-
GENERATOR_MARKER_FALLBACK,
|
|
492
|
-
resolveGeneratorMarker,
|
|
493
|
-
writeIDEConfigs,
|
|
494
|
-
},
|
|
522
|
+
_internal: { writeIDEConfigs, generateConfigYaml },
|
|
495
523
|
};
|
|
496
524
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
*
|
|
516
|
-
* @param {object} args
|
|
517
|
-
* @param {string} args.projectDir - Absolute path to the target project
|
|
518
|
-
* @param {string[]} args.detectedIDEs - IDE ids to configure (keys of IDE_CONFIGS)
|
|
519
|
-
* @param {string} args.configContent - Rendered config body to write
|
|
520
|
-
* @param {boolean} [args.yes] - Non-interactive mode (--yes)
|
|
521
|
-
* @returns {{written: string[], skipped: string[], backedUp: string[]}} config file names per outcome
|
|
522
|
-
*/
|
|
523
|
-
function writeIDEConfigs({ projectDir, detectedIDEs, configContent, yes }) {
|
|
524
|
-
const generatorMarker = resolveGeneratorMarker();
|
|
525
|
-
// Several IDEs share one file (gemini-cli + antigravity → GEMINI.md): dedupe
|
|
526
|
-
// outcomes by file name so a shared file is reported — and warned about — once.
|
|
527
|
-
const outcome = { written: new Set(), skipped: new Set(), backedUp: new Set() };
|
|
528
|
-
|
|
529
|
-
for (const ideId of detectedIDEs) {
|
|
530
|
-
const ide = IDE_CONFIGS[ideId];
|
|
531
|
-
if (!ide) continue;
|
|
532
|
-
|
|
533
|
-
const configPath = path.join(projectDir, ide.configFile);
|
|
534
|
-
|
|
535
|
-
if (fs.existsSync(configPath)) {
|
|
536
|
-
const existing = fs.readFileSync(configPath, 'utf8');
|
|
537
|
-
|
|
538
|
-
// PROD-01: the adapter generator owns this file — defer to it.
|
|
539
|
-
if (existing.includes(generatorMarker)) {
|
|
540
|
-
if (!outcome.skipped.has(ide.configFile)) {
|
|
541
|
-
clack.log.warn(
|
|
542
|
-
`⚠️ ${ide.configFile} is generated by tools/build/generate-adapters.js — left untouched. ` +
|
|
543
|
-
`Run \`node tools/build/generate-adapters.js --adopt\` to refresh it from registry.yaml.`
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
outcome.skipped.add(ide.configFile);
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
// NODE-02: never clobber a hand-authored config without protecting it.
|
|
551
|
-
if (!existing.includes(INSTALLER_MARKER)) {
|
|
552
|
-
if (!yes) {
|
|
553
|
-
// Interactive: keep the user's file untouched.
|
|
554
|
-
if (!outcome.skipped.has(ide.configFile)) {
|
|
555
|
-
clack.log.warn(`⚠️ ${ide.configFile} already exists and was not created by BMAD+ — kept your file (skipped). Re-run with --yes to back it up and overwrite.`);
|
|
556
|
-
}
|
|
557
|
-
outcome.skipped.add(ide.configFile);
|
|
558
|
-
continue;
|
|
559
|
-
}
|
|
560
|
-
// Non-interactive: back up before overwriting, never destroy data.
|
|
561
|
-
if (!outcome.backedUp.has(ide.configFile)) {
|
|
562
|
-
let backupPath = `${configPath}.bak`;
|
|
563
|
-
if (fs.existsSync(backupPath)) backupPath = `${configPath}.${Date.now()}.bak`;
|
|
564
|
-
fs.copyFileSync(configPath, backupPath);
|
|
565
|
-
clack.log.warn(`⚠️ Backed up existing ${ide.configFile} → ${path.basename(backupPath)}`);
|
|
566
|
-
outcome.backedUp.add(ide.configFile);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
fs.writeFileSync(configPath, configContent, 'utf8');
|
|
572
|
-
outcome.written.add(ide.configFile);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
return {
|
|
576
|
-
written: [...outcome.written],
|
|
577
|
-
skipped: [...outcome.skipped],
|
|
578
|
-
backedUp: [...outcome.backedUp],
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
function generateConfigYaml(userName, language, projectDir) {
|
|
583
|
-
const projectName = path.basename(projectDir);
|
|
584
|
-
return `# BMAD+ Project Configuration
|
|
585
|
-
# Generated by bmad-plus install
|
|
586
|
-
|
|
587
|
-
user_name: "${userName}"
|
|
588
|
-
communication_language: "${language}"
|
|
589
|
-
document_output_language: "${language}"
|
|
590
|
-
output_folder: "_bmad-output"
|
|
591
|
-
project_name: "${projectName}"
|
|
592
|
-
|
|
593
|
-
# Execution settings
|
|
594
|
-
execution_mode: "manual"
|
|
595
|
-
auto_role_activation: true
|
|
596
|
-
parallel_execution: true
|
|
597
|
-
`;
|
|
525
|
+
function generateConfigYaml(userName, language, projectDir, execMode = 'manual') {
|
|
526
|
+
if (!['manual', 'autopilot', 'hybrid'].includes(execMode)) throw new Error('Unknown execution mode: ' + execMode);
|
|
527
|
+
return '# BMAD+ Project Configuration\n# Generated by bmad-plus install\n\n' + yaml.dump({
|
|
528
|
+
user_name: userName,
|
|
529
|
+
communication_language: language,
|
|
530
|
+
document_output_language: language,
|
|
531
|
+
output_folder: '_bmad-output',
|
|
532
|
+
project_name: path.basename(projectDir),
|
|
533
|
+
execution_mode: execMode,
|
|
534
|
+
auto_role_activation: true,
|
|
535
|
+
parallel_execution: true,
|
|
536
|
+
checkpoints: {
|
|
537
|
+
discovery: 'require_approval',
|
|
538
|
+
architecture: 'require_approval',
|
|
539
|
+
story: execMode === 'autopilot' ? 'notify_only' : 'require_approval',
|
|
540
|
+
delivery: 'require_approval',
|
|
541
|
+
},
|
|
542
|
+
}, { lineWidth: -1, forceQuotes: true, quotingType: '"' });
|
|
598
543
|
}
|