bmad-plus 0.12.0 → 0.12.2
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 +612 -554
- package/README.md +557 -577
- package/osint-agent-package/agents/osint-investigator.md +12 -0
- package/osint-agent-package/skills/bmad-osint-investigate/osint/SKILL.md +491 -482
- package/osint-agent-package/skills/bmad-osint-investigate/osint/assets/dossier-template.md +126 -126
- package/osint-agent-package/skills/bmad-osint-investigate/osint/assets/lawful-basis-record.md +48 -48
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/content-extraction.md +100 -100
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/gdpr-osint.md +48 -48
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/platforms.md +130 -130
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/psychoprofile.md +69 -69
- package/osint-agent-package/skills/bmad-osint-investigate/osint/references/tools.md +281 -281
- package/osint-agent-package/skills/bmad-osint-investigate/osint/scripts/mcp-client.py +136 -136
- package/package.json +104 -91
- package/readme-international/README.de.md +596 -589
- package/readme-international/README.es.md +613 -606
- package/readme-international/README.fr.md +611 -604
- package/src/bmad-plus/agents/agent-shadow/SKILL.md +18 -0
- package/src/bmad-plus/data/role-triggers.yaml +52 -0
- package/src/bmad-plus/module.yaml +283 -283
- package/src/bmad-plus/packs/pack-animated/animated-website-agent.md +325 -325
- package/src/bmad-plus/packs/pack-animated/templates/animated-website-workflow.md +55 -55
- package/src/bmad-plus/packs/pack-backup/backup-agent.md +71 -71
- package/src/bmad-plus/packs/pack-backup/templates/backup-workflow.md +51 -51
- package/src/bmad-plus/packs/pack-dev-studio/README.md +162 -162
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +73 -73
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +61 -61
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +95 -95
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +95 -95
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +134 -134
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +80 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +73 -73
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +95 -95
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +73 -73
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +73 -73
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +92 -92
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +80 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +90 -90
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +153 -153
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +173 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +224 -224
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +329 -329
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +318 -318
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +359 -359
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +379 -379
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +361 -361
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +81 -81
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +67 -67
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +85 -85
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +35 -35
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +49 -49
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +131 -131
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +89 -89
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +300 -300
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +428 -428
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +73 -73
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +80 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +484 -484
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +193 -193
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +175 -175
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +110 -110
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +1511 -1511
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +298 -298
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +296 -296
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +29 -29
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +74 -74
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +29 -29
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +73 -73
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +89 -89
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +73 -73
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +29 -29
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +141 -141
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +37 -37
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +75 -75
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +6 -6
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +110 -110
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +176 -176
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +67 -67
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +86 -86
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +179 -179
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +66 -66
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +127 -127
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +105 -105
- package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +120 -120
- package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +12 -12
- package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +328 -328
- package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +32 -32
- package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +81 -81
- package/src/bmad-plus/packs/pack-memory/shared/memory-protocol.md +14 -0
- package/src/bmad-plus/packs/pack-seo/SKILL.md +171 -171
- package/src/bmad-plus/packs/pack-seo/checklist.md +140 -140
- package/src/bmad-plus/packs/pack-seo/pagespeed-playbook.md +320 -320
- package/src/bmad-plus/packs/pack-seo/ref/audit-schema.json +187 -187
- package/src/bmad-plus/packs/pack-seo/ref/cwv-thresholds.md +87 -87
- package/src/bmad-plus/packs/pack-seo/ref/eeat-criteria.md +123 -123
- package/src/bmad-plus/packs/pack-seo/ref/geo-signals.md +167 -167
- package/src/bmad-plus/packs/pack-seo/ref/hreflang-rules.md +153 -153
- package/src/bmad-plus/packs/pack-seo/ref/quality-gates.md +133 -133
- package/src/bmad-plus/packs/pack-seo/ref/schema-catalog.md +91 -91
- package/src/bmad-plus/packs/pack-seo/ref/schema-templates.json +356 -356
- package/src/bmad-plus/packs/pack-seo/requirements.txt +17 -0
- package/src/bmad-plus/packs/pack-seo/scripts/seo_apis.py +456 -0
- package/src/bmad-plus/packs/pack-seo/scripts/seo_crawl.py +359 -0
- package/src/bmad-plus/packs/pack-seo/scripts/seo_fetch.py +304 -0
- package/src/bmad-plus/packs/pack-seo/scripts/seo_parse.py +255 -0
- package/src/bmad-plus/packs/pack-seo/scripts/seo_report.py +410 -0
- package/src/bmad-plus/packs/pack-seo/scripts/seo_screenshot.py +202 -0
- package/src/bmad-plus/packs/pack-seo/seo-chief.md +294 -294
- package/src/bmad-plus/packs/pack-seo/seo-judge.md +241 -241
- package/src/bmad-plus/packs/pack-seo/seo-scout.md +171 -171
- package/src/bmad-plus/packs/pack-seo/templates/seo-audit-workflow.md +241 -241
- package/src/bmad-plus/packs/pack-shield/README.md +6 -6
- package/src/bmad-plus/packs/pack-shield/SKILL.md +2 -2
- package/src/bmad-plus/packs/pack-shield/categories/accessibility-esg/csrd-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/accessibility-esg/section508-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/accessibility-esg/wcag-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/ai-governance/eu-ai-act-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/ai-governance/iso42001-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/ai-governance/nist-ai-rmf-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/cis-controls-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/ism-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/iso27001-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/nis2-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/nist-800-53-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/nist-csf-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/defense-export/cmmc-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/defense-export/ear-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/defense-export/itar-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/defense-export/tsa-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/dora-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/fedramp-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/hipaa-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/pci-dss-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/soc2-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/swift-csp-agent.md +11 -11
- package/src/bmad-plus/packs/pack-shield/shield-orchestrator.md +1 -1
- package/tools/build/check-counts.js +628 -0
- package/tools/build/generated-adapters/.codex/AGENTS.md +1 -1
- package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +1 -1
- package/tools/build/generated-adapters/.opencode/AGENTS.md +1 -1
- package/tools/build/generated-adapters/AGENTS.md +1 -1
- package/tools/build/generated-adapters/CLAUDE.md +1 -1
- package/tools/build/generated-adapters/CONVENTIONS.md +1 -1
- package/tools/build/generated-adapters/GEMINI.md +1 -1
- package/tools/cli/commands/autoconfig.js +470 -472
- package/tools/cli/commands/doctor.js +233 -233
- package/tools/cli/commands/install.js +598 -495
- package/tools/cli/commands/memory-journal-cmd.js +311 -311
- package/tools/cli/commands/memory.js +195 -195
- package/tools/cli/commands/scan.js +348 -364
- package/tools/cli/commands/uninstall.js +101 -101
- package/tools/cli/commands/update.js +134 -134
- package/tools/cli/i18n.js +845 -845
- package/tools/cli/lib/README-memory-journal.md +125 -125
- package/tools/cli/lib/ide-config.js +267 -259
- package/tools/cli/lib/memory-init.js +204 -113
- package/tools/cli/lib/packs.js +209 -209
- package/tools/cli/lib/path-hash.js +39 -0
- package/tools/cli/lib/python-provision.js +508 -508
- package/tools/cli/lib/stack-detect.js +102 -102
- package/tools/cli/lib/validate.js +50 -50
|
@@ -1,495 +1,598 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BMAD+ Install Command
|
|
3
|
-
* Installs agents, skills, and IDE configs into the current project
|
|
4
|
-
* Supports 10 languages: EN, FR, ES, DE, PT-BR, RU, ZH, HE, JA, IT
|
|
5
|
-
*
|
|
6
|
-
* Author: Laurent Rochetta
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const path = require('node:path');
|
|
10
|
-
const fs = require('node:fs');
|
|
11
|
-
const fsExtra = require('fs-extra');
|
|
12
|
-
const clack = require('@clack/prompts');
|
|
13
|
-
const pc = require('picocolors');
|
|
14
|
-
const { t, getLanguageOptions, getCommLanguageOptions } = require('../i18n');
|
|
15
|
-
const { PACKS } = require('../lib/packs');
|
|
16
|
-
const { copyPackFiles } = require('../lib/pack-copy');
|
|
17
|
-
const { initMemory } = require('../lib/memory-init');
|
|
18
|
-
const { generateIDEConfig, IDE_CONFIGS } = require('../lib/ide-config');
|
|
19
|
-
const { validateUserName } = require('../lib/validate');
|
|
20
|
-
const { provisionPack } = require('../lib/python-provision');
|
|
21
|
-
|
|
22
|
-
// Packs whose runtime includes Python (registry.yaml: runtime includes "python").
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
requirements: ['
|
|
32
|
-
verifyModules: ['
|
|
33
|
-
},
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
detectedIDEs =
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
i.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
'
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
);
|
|
447
|
-
}
|
|
448
|
-
if (selectedPacks.includes('
|
|
449
|
-
examples.push(
|
|
450
|
-
` ${i.
|
|
451
|
-
` ${i.
|
|
452
|
-
` ${i.
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
''
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* BMAD+ Install Command
|
|
3
|
+
* Installs agents, skills, and IDE configs into the current project
|
|
4
|
+
* Supports 10 languages: EN, FR, ES, DE, PT-BR, RU, ZH, HE, JA, IT
|
|
5
|
+
*
|
|
6
|
+
* Author: Laurent Rochetta
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const path = require('node:path');
|
|
10
|
+
const fs = require('node:fs');
|
|
11
|
+
const fsExtra = require('fs-extra');
|
|
12
|
+
const clack = require('@clack/prompts');
|
|
13
|
+
const pc = require('picocolors');
|
|
14
|
+
const { t, getLanguageOptions, getCommLanguageOptions } = require('../i18n');
|
|
15
|
+
const { PACKS } = require('../lib/packs');
|
|
16
|
+
const { copyPackFiles } = require('../lib/pack-copy');
|
|
17
|
+
const { initMemory } = require('../lib/memory-init');
|
|
18
|
+
const { generateIDEConfig, IDE_CONFIGS } = require('../lib/ide-config');
|
|
19
|
+
const { validateUserName } = require('../lib/validate');
|
|
20
|
+
const { provisionPack } = require('../lib/python-provision');
|
|
21
|
+
|
|
22
|
+
// Packs whose runtime includes Python (registry.yaml: runtime includes "python").
|
|
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
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = {
|
|
70
|
+
command: 'install',
|
|
71
|
+
description: 'Install BMAD+ agents and skills into your project',
|
|
72
|
+
options: [
|
|
73
|
+
['-d, --directory <path>', 'Installation directory (default: current directory)'],
|
|
74
|
+
['-p, --packs <packs>', 'Comma-separated pack IDs: core,osint,all (default: interactive)'],
|
|
75
|
+
['-y, --yes', 'Accept all defaults, skip prompts'],
|
|
76
|
+
['-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
|
+
['--provision-python', 'Provision an isolated Python env for packs that need one (seo, memory)'],
|
|
79
|
+
],
|
|
80
|
+
action: async (options) => {
|
|
81
|
+
const projectDir = path.resolve(options.directory || process.cwd());
|
|
82
|
+
const bmadSrc = path.join(__dirname, '..', '..', '..', 'src', 'bmad-plus');
|
|
83
|
+
|
|
84
|
+
// ── Step 0: Language Selection ──
|
|
85
|
+
const pkgJson = require('../../../package.json');
|
|
86
|
+
clack.intro(pc.bgCyan(pc.black(` BMAD+ Installer v${pkgJson.version} `)));
|
|
87
|
+
clack.log.info(pc.dim('✨ Created by Laurent Rochetta — github.com/lrochetta/BMAD-PLUS'));
|
|
88
|
+
|
|
89
|
+
let lang = options.lang || 'en';
|
|
90
|
+
if (!options.yes && !options.lang) {
|
|
91
|
+
const langChoice = await clack.select({
|
|
92
|
+
message: '🌐 Select your language / Choisissez votre langue / 选择语言',
|
|
93
|
+
options: getLanguageOptions(),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (clack.isCancel(langChoice)) {
|
|
97
|
+
clack.cancel('Installation cancelled.');
|
|
98
|
+
throw new Error('Installation cancelled.');
|
|
99
|
+
}
|
|
100
|
+
lang = langChoice;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const i = t(lang); // Get translations for selected language
|
|
104
|
+
|
|
105
|
+
// Verify source exists
|
|
106
|
+
if (!fs.existsSync(bmadSrc)) {
|
|
107
|
+
clack.log.error(`${i.source_not_found}: ${bmadSrc}`);
|
|
108
|
+
clack.outro(pc.red(i.failed));
|
|
109
|
+
throw new Error(`Source not found: ${bmadSrc}`);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
clack.log.info(`${i.installing_to}: ${pc.cyan(projectDir)}`);
|
|
113
|
+
|
|
114
|
+
// ── Step 1: Pack Selection ──
|
|
115
|
+
let selectedPacks = ['core']; // Core always included
|
|
116
|
+
|
|
117
|
+
if (options.packs) {
|
|
118
|
+
const requested = options.packs.split(',').map(p => p.trim());
|
|
119
|
+
if (requested.includes('all')) {
|
|
120
|
+
selectedPacks = Object.keys(PACKS).filter(k => !PACKS[k].disabled);
|
|
121
|
+
} else {
|
|
122
|
+
selectedPacks = [...new Set(['core', ...requested.filter(p => PACKS[p] && !PACKS[p].disabled)])];
|
|
123
|
+
}
|
|
124
|
+
} else if (!options.yes) {
|
|
125
|
+
const packChoice = await clack.multiselect({
|
|
126
|
+
message: i.select_packs,
|
|
127
|
+
options: Object.entries(PACKS)
|
|
128
|
+
.filter(([, p]) => !p.required)
|
|
129
|
+
.map(([key, pack]) => ({
|
|
130
|
+
value: key,
|
|
131
|
+
label: `${pack.icon} ${pack.name}`,
|
|
132
|
+
hint: pack.disabled ? i.soon : (pack.desc || pack.description || ''),
|
|
133
|
+
disabled: pack.disabled,
|
|
134
|
+
})),
|
|
135
|
+
required: false,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (clack.isCancel(packChoice)) {
|
|
139
|
+
clack.cancel(i.cancelled);
|
|
140
|
+
throw new Error(i.cancelled);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
selectedPacks = [...new Set(['core', ...packChoice])];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
clack.log.success(`${i.selected_packs}: ${selectedPacks.map(p => `${PACKS[p].icon} ${PACKS[p].name}`).join(', ')}`);
|
|
147
|
+
|
|
148
|
+
// ── Step 2: IDE Detection ──
|
|
149
|
+
let detectedIDEs = [];
|
|
150
|
+
|
|
151
|
+
if (options.tools) {
|
|
152
|
+
if (options.tools === 'none' || options.tools === 'skip') {
|
|
153
|
+
detectedIDEs = [];
|
|
154
|
+
} else {
|
|
155
|
+
detectedIDEs = options.tools.split(',').map(t => t.trim()).filter(t => IDE_CONFIGS[t]);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
// Auto-detect
|
|
159
|
+
for (const [id, ide] of Object.entries(IDE_CONFIGS)) {
|
|
160
|
+
for (const marker of ide.detect) {
|
|
161
|
+
if (fs.existsSync(path.join(projectDir, marker))) {
|
|
162
|
+
detectedIDEs.push(id);
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// If nothing detected, ask
|
|
169
|
+
if (detectedIDEs.length === 0 && !options.yes) {
|
|
170
|
+
const ideChoice = await clack.multiselect({
|
|
171
|
+
message: i.select_ide,
|
|
172
|
+
options: Object.entries(IDE_CONFIGS).map(([key, ide]) => ({
|
|
173
|
+
value: key,
|
|
174
|
+
label: ide.name,
|
|
175
|
+
})),
|
|
176
|
+
required: false,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
if (!clack.isCancel(ideChoice)) {
|
|
180
|
+
detectedIDEs = ideChoice;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Default to all if --yes
|
|
185
|
+
if (detectedIDEs.length === 0 && options.yes) {
|
|
186
|
+
detectedIDEs = Object.keys(IDE_CONFIGS);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (detectedIDEs.length > 0) {
|
|
191
|
+
clack.log.info(`${i.detected_ides}: ${detectedIDEs.map(id => IDE_CONFIGS[id].name).join(', ')}`);
|
|
192
|
+
} else if (options.tools === 'none' || options.tools === 'skip') {
|
|
193
|
+
clack.log.info(pc.dim('⏭️ IDE config skipped (--tools none) — existing configs preserved'));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ── Step 3: User Config ──
|
|
197
|
+
let userName = process.env.USER || process.env.USERNAME || 'Developer';
|
|
198
|
+
let commLang = 'French';
|
|
199
|
+
|
|
200
|
+
if (!options.yes) {
|
|
201
|
+
const userConfig = await clack.group({
|
|
202
|
+
userName: () => clack.text({
|
|
203
|
+
message: i.enter_name,
|
|
204
|
+
placeholder: userName,
|
|
205
|
+
defaultValue: userName,
|
|
206
|
+
}),
|
|
207
|
+
commLang: () => clack.select({
|
|
208
|
+
message: i.comm_language,
|
|
209
|
+
options: getCommLanguageOptions(),
|
|
210
|
+
}),
|
|
211
|
+
execMode: () => clack.select({
|
|
212
|
+
message: i.exec_mode,
|
|
213
|
+
options: [
|
|
214
|
+
{ value: 'manual', label: i.exec_manual },
|
|
215
|
+
{ value: 'autopilot', label: i.exec_autopilot },
|
|
216
|
+
{ value: 'hybrid', label: i.exec_hybrid },
|
|
217
|
+
],
|
|
218
|
+
}),
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
if (clack.isCancel(userConfig)) {
|
|
222
|
+
clack.cancel(i.cancelled);
|
|
223
|
+
throw new Error(i.cancelled);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Validate user-provided name
|
|
227
|
+
const rawName = userConfig.userName;
|
|
228
|
+
const fb = process.env.USER || process.env.USERNAME || 'Developer';
|
|
229
|
+
const { name: validatedName, warnings } = validateUserName(rawName, fb);
|
|
230
|
+
for (const w of warnings) clack.log.warn(w);
|
|
231
|
+
userName = validatedName;
|
|
232
|
+
commLang = userConfig.commLang;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ── Step 4: Install Files ──
|
|
236
|
+
const spinner = clack.spinner();
|
|
237
|
+
spinner.start(i.installing_files);
|
|
238
|
+
|
|
239
|
+
const targetAgentsDir = path.join(projectDir, '.agents', 'skills');
|
|
240
|
+
const targetDataDir = path.join(projectDir, '.agents', 'data');
|
|
241
|
+
const targetBmadDir = path.join(projectDir, '_bmad');
|
|
242
|
+
|
|
243
|
+
// Create directories
|
|
244
|
+
fsExtra.ensureDirSync(targetAgentsDir);
|
|
245
|
+
fsExtra.ensureDirSync(targetDataDir);
|
|
246
|
+
fsExtra.ensureDirSync(targetBmadDir);
|
|
247
|
+
|
|
248
|
+
let copiedAgents = 0;
|
|
249
|
+
let copiedSkills = 0;
|
|
250
|
+
let copiedFiles = 0;
|
|
251
|
+
|
|
252
|
+
const projectRoot = path.join(bmadSrc, '..', '..');
|
|
253
|
+
|
|
254
|
+
for (const packId of selectedPacks) {
|
|
255
|
+
const pack = PACKS[packId];
|
|
256
|
+
if (!pack || pack.disabled) continue;
|
|
257
|
+
|
|
258
|
+
const result = copyPackFiles({
|
|
259
|
+
bmadSrc,
|
|
260
|
+
targetAgentsDir,
|
|
261
|
+
targetDataDir,
|
|
262
|
+
projectRoot,
|
|
263
|
+
pack,
|
|
264
|
+
});
|
|
265
|
+
copiedAgents += result.copiedAgents;
|
|
266
|
+
copiedSkills += result.copiedSkills;
|
|
267
|
+
copiedFiles += result.copiedFiles;
|
|
268
|
+
|
|
269
|
+
// Memory pack: initialize brain with existing brain detection.
|
|
270
|
+
// Non-fatal: a read-only or missing home dir (containers, CI) must not
|
|
271
|
+
// abort the install mid-flight and leave a manifest-less partial tree.
|
|
272
|
+
if (packId === 'memory' && pack.packDir) {
|
|
273
|
+
try {
|
|
274
|
+
initMemory({ projectDir, bmadSrc, userName, commLang, selectedPacks });
|
|
275
|
+
} catch (err) {
|
|
276
|
+
clack.log.warn(`Memory/brain initialization failed (non-fatal, install continues): ${err.message}`);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ── Python provisioning (opt-in) — for packs whose runtime needs Python ──
|
|
282
|
+
// Off by default so `install` never spawns Python unless asked. Closes the
|
|
283
|
+
// "SEO pack ships prompts that call scripts never installed" gap (PROD-02).
|
|
284
|
+
if (options.provisionPython) {
|
|
285
|
+
const pythonPacks = selectedPacks.filter((p) => PYTHON_PACKS[p]);
|
|
286
|
+
for (const packId of pythonPacks) {
|
|
287
|
+
const cfg = PYTHON_PACKS[packId];
|
|
288
|
+
const requirementsPath = path.join(projectRoot, ...cfg.requirements);
|
|
289
|
+
if (!fs.existsSync(requirementsPath)) {
|
|
290
|
+
clack.log.warn(`Python provisioning skipped for ${packId}: ${requirementsPath} not found.`);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const pySpinner = clack.spinner();
|
|
294
|
+
pySpinner.start(`Provisioning Python runtime for ${PACKS[packId].name}…`);
|
|
295
|
+
const res = provisionPack({
|
|
296
|
+
envDir: path.join(projectDir, '.bmad', 'venv', packId),
|
|
297
|
+
requirementsPath,
|
|
298
|
+
verifyModules: cfg.verifyModules,
|
|
299
|
+
});
|
|
300
|
+
pySpinner.stop(
|
|
301
|
+
res.ok
|
|
302
|
+
? `${PACKS[packId].name}: Python runtime ready (${res.tool}).`
|
|
303
|
+
: `${PACKS[packId].name}: Python not provisioned — ${res.messages[res.messages.length - 1] || 'see guidance'}`
|
|
304
|
+
);
|
|
305
|
+
if (!res.ok) {
|
|
306
|
+
clack.log.warn(`Run \`${PACKS[packId].name}\` Python tools after installing Python ≥3.11; the pack's prompts still work, only the local scripts need the runtime.`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Copy module config
|
|
312
|
+
const moduleYaml = path.join(bmadSrc, 'module.yaml');
|
|
313
|
+
if (fs.existsSync(moduleYaml)) {
|
|
314
|
+
fsExtra.copySync(moduleYaml, path.join(targetBmadDir, 'module.yaml'));
|
|
315
|
+
copiedFiles++;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const helpCsv = path.join(bmadSrc, 'module-help.csv');
|
|
319
|
+
if (fs.existsSync(helpCsv)) {
|
|
320
|
+
fsExtra.copySync(helpCsv, path.join(targetBmadDir, 'module-help.csv'));
|
|
321
|
+
copiedFiles++;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
spinner.stop(i.installed_summary(copiedAgents, copiedSkills, copiedFiles));
|
|
325
|
+
|
|
326
|
+
// ── Step 5: Generate IDE Configs ──
|
|
327
|
+
if (detectedIDEs.length > 0) {
|
|
328
|
+
const ideSpinner = clack.spinner();
|
|
329
|
+
ideSpinner.start(i.configuring_ides);
|
|
330
|
+
|
|
331
|
+
writeIDEConfigs({
|
|
332
|
+
projectDir,
|
|
333
|
+
detectedIDEs,
|
|
334
|
+
configContent: generateIDEConfig(userName, commLang, selectedPacks),
|
|
335
|
+
yes: options.yes,
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
ideSpinner.stop(i.ide_configured(detectedIDEs.length));
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ── Step 6: Create config.yaml ──
|
|
342
|
+
const configYaml = generateConfigYaml(userName, commLang, projectDir);
|
|
343
|
+
const configPath = path.join(targetBmadDir, 'config.yaml');
|
|
344
|
+
fs.writeFileSync(configPath, configYaml, 'utf8');
|
|
345
|
+
|
|
346
|
+
// ── Step 7: Create output directories ──
|
|
347
|
+
const outputDir = path.join(projectDir, '_bmad-output');
|
|
348
|
+
fsExtra.ensureDirSync(path.join(outputDir, 'discovery'));
|
|
349
|
+
fsExtra.ensureDirSync(path.join(outputDir, 'build'));
|
|
350
|
+
fsExtra.ensureDirSync(path.join(projectDir, 'docs'));
|
|
351
|
+
|
|
352
|
+
// ── Step 8: Write install manifest ──
|
|
353
|
+
const pkgVersion = require('../../../package.json').version;
|
|
354
|
+
const manifest = {
|
|
355
|
+
version: pkgVersion,
|
|
356
|
+
uiLanguage: lang,
|
|
357
|
+
installed: new Date().toISOString(),
|
|
358
|
+
packs: selectedPacks,
|
|
359
|
+
ides: detectedIDEs,
|
|
360
|
+
user: userName,
|
|
361
|
+
language: commLang,
|
|
362
|
+
};
|
|
363
|
+
fs.writeFileSync(
|
|
364
|
+
path.join(targetBmadDir, '.bmad-plus-install.json'),
|
|
365
|
+
JSON.stringify(manifest, null, 2),
|
|
366
|
+
'utf8'
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
// ── Summary — Contextual Getting Started ──
|
|
370
|
+
const agentGuide = [
|
|
371
|
+
i.guide_who,
|
|
372
|
+
'',
|
|
373
|
+
` ${i.guide_idea.padEnd(28)} → "Atlas, [...]"`,
|
|
374
|
+
` ${i.guide_prd.padEnd(28)} → "Atlas, create PRD"`,
|
|
375
|
+
` ${i.guide_arch.padEnd(28)} → "Forge, propose architecture"`,
|
|
376
|
+
` ${i.guide_code.padEnd(28)} → "Forge, implement story [X]"`,
|
|
377
|
+
` ${i.guide_test.padEnd(28)} → "Sentinel, review module [X]"`,
|
|
378
|
+
` ${i.guide_sprint.padEnd(28)} → "Nexus, create epics"`,
|
|
379
|
+
` ${i.guide_auto.padEnd(28)} → "autopilot"`,
|
|
380
|
+
];
|
|
381
|
+
|
|
382
|
+
if (selectedPacks.includes('osint')) {
|
|
383
|
+
agentGuide.push(` ${i.guide_osint.padEnd(28)} → "Shadow, investigate [name]"`);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (selectedPacks.includes('maker')) {
|
|
387
|
+
agentGuide.push(` ${i.guide_maker.padEnd(28)} → "Maker, create agent [desc]"`);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (selectedPacks.includes('seo')) {
|
|
391
|
+
agentGuide.push(` ${i.guide_seo.padEnd(28)} → "/seo audit <url>"`);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (selectedPacks.includes('backup')) {
|
|
395
|
+
agentGuide.push(` ${i.guide_backup.padEnd(28)} → "/backup create"`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (selectedPacks.includes('animated')) {
|
|
399
|
+
agentGuide.push(` ${i.guide_animated.padEnd(28)} → "/animated build <video>"`);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (selectedPacks.includes('shield')) {
|
|
403
|
+
agentGuide.push(` ${(i.guide_shield || '🛡️ GRC Compliance').padEnd(28)} → "Shield, audit my SaaS for GDPR"`);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (selectedPacks.includes('memory')) {
|
|
407
|
+
agentGuide.push(` ${(i.guide_memory || '🧠 Memory Brain').padEnd(28)} → "Zecher, scan projects in [path]"`);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (selectedPacks.includes('dev-studio')) {
|
|
411
|
+
agentGuide.push(` ${(i.guide_dev_studio || '🏗️ Dev Studio').padEnd(28)} → "Miriam, brainstorm my app idea"`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
agentGuide.push(
|
|
415
|
+
'',
|
|
416
|
+
i.guide_workflow,
|
|
417
|
+
' 1. Atlas (idea → brief → PRD)',
|
|
418
|
+
' 2. Forge (architecture → code)',
|
|
419
|
+
' 3. Sentinel (tests → review)',
|
|
420
|
+
'',
|
|
421
|
+
i.guide_or_auto,
|
|
422
|
+
'',
|
|
423
|
+
`${i.guide_output}: _bmad-output/discovery/ & _bmad-output/build/`,
|
|
424
|
+
'',
|
|
425
|
+
'─'.repeat(50),
|
|
426
|
+
'',
|
|
427
|
+
`📦 ${i.guide_cli_title || 'CLI Commands'}:`,
|
|
428
|
+
` npx bmad-plus install ${i.guide_cli_install || '— Install agents & skills'}`,
|
|
429
|
+
` npx bmad-plus update ${i.guide_cli_update || '— Update agents (keeps config)'}`,
|
|
430
|
+
` npx bmad-plus doctor ${i.guide_cli_doctor || '— Check installation health'}`,
|
|
431
|
+
` npx bmad-plus uninstall ${i.guide_cli_uninstall || '— Remove BMAD+ from project'}`,
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
// Add pack-specific examples
|
|
435
|
+
const examples = [];
|
|
436
|
+
if (selectedPacks.includes('seo')) {
|
|
437
|
+
examples.push(` ${i.guide_example_seo || '🔍 SEO: "/seo audit https://example.com"'}`);
|
|
438
|
+
}
|
|
439
|
+
if (selectedPacks.includes('backup')) {
|
|
440
|
+
examples.push(` ${i.guide_example_backup || '🗂️ Backup: "/backup create" → ZIP timestamped'}`);
|
|
441
|
+
}
|
|
442
|
+
if (selectedPacks.includes('animated')) {
|
|
443
|
+
examples.push(` ${i.guide_example_animated || '🎬 Animated: "/animated build hero.mp4"'}`);
|
|
444
|
+
}
|
|
445
|
+
if (selectedPacks.includes('osint')) {
|
|
446
|
+
examples.push(` ${i.guide_example_osint || '🔍 OSINT: "Shadow, investigate John Doe"'}`);
|
|
447
|
+
}
|
|
448
|
+
if (selectedPacks.includes('shield')) {
|
|
449
|
+
examples.push(
|
|
450
|
+
` ${i.guide_example_shield_1 || '🛡️ GRC: "Shield, audit my app for GDPR compliance"'}`,
|
|
451
|
+
` ${i.guide_example_shield_2 || '🛡️ GRC: "Shield, gap analysis ISO 27001 vs NIST CSF"'}`,
|
|
452
|
+
` ${i.guide_example_shield_3 || '🛡️ GRC: "Shield, generate SOC 2 evidence checklist"'}`,
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
if (selectedPacks.includes('dev-studio')) {
|
|
456
|
+
examples.push(
|
|
457
|
+
` ${i.guide_example_dev_studio_1 || '🏗️ Dev Studio: "Miriam, brainstorm a productivity app"'}`,
|
|
458
|
+
` ${i.guide_example_dev_studio_2 || '🏗️ Dev Studio: "Bezalel, design the architecture"'}`,
|
|
459
|
+
` ${i.guide_example_dev_studio_3 || '🏗️ Dev Studio: "Oholiab, implement story S1"'}`,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
if (selectedPacks.includes('memory')) {
|
|
463
|
+
examples.push(
|
|
464
|
+
` ${i.guide_example_memory_1 || '🧠 Memory: "Zecher, scan projects in D:\\travail\\DEV"'}`,
|
|
465
|
+
` ${i.guide_example_memory_2 || '🧠 Memory: "Zecher, where were we?"'}`,
|
|
466
|
+
` ${i.guide_example_memory_3 || '🧠 Memory: "Zecher, consolidate memory"'}`,
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (examples.length > 0) {
|
|
471
|
+
agentGuide.push(
|
|
472
|
+
'',
|
|
473
|
+
`💡 ${i.guide_examples_title || 'Quick Examples'}:`,
|
|
474
|
+
...examples
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
agentGuide.push(
|
|
479
|
+
'',
|
|
480
|
+
'---',
|
|
481
|
+
i.guide_credits
|
|
482
|
+
);
|
|
483
|
+
|
|
484
|
+
clack.note(agentGuide.join('\n'), i.guide_title);
|
|
485
|
+
|
|
486
|
+
clack.outro(pc.green(i.guide_ready));
|
|
487
|
+
},
|
|
488
|
+
// Exported for functional testing (not part of the public API)
|
|
489
|
+
_internal: {
|
|
490
|
+
INSTALLER_MARKER,
|
|
491
|
+
GENERATOR_MARKER_FALLBACK,
|
|
492
|
+
resolveGeneratorMarker,
|
|
493
|
+
writeIDEConfigs,
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
// ── Helpers ──
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Write the IDE config files, never destroying one this installer does not own.
|
|
502
|
+
*
|
|
503
|
+
* An existing config falls into one of three classes, tested in this order:
|
|
504
|
+
*
|
|
505
|
+
* 1. GENERATOR-MANAGED (carries the adapter generator's marker) — SKIPPED, even
|
|
506
|
+
* under --yes. Such a file is a pure function of registry.yaml +
|
|
507
|
+
* tools/build/adapters.config.js, and the installer template is a strict
|
|
508
|
+
* subset of it (no registry facts), so overwriting is a downgrade that also
|
|
509
|
+
* breaks the `generate-adapters.js --check` drift gate. A .bak does not
|
|
510
|
+
* rescue it: every tool reads the live path, not the backup. The repair is
|
|
511
|
+
* regeneration, not merging — so install defers and points at the generator.
|
|
512
|
+
* 2. HAND-AUTHORED (no BMAD+ marker) — kept as-is interactively; backed up then
|
|
513
|
+
* overwritten under --yes (NODE-02).
|
|
514
|
+
* 3. INSTALLER-GENERATED (carries INSTALLER_MARKER) — refreshed in place.
|
|
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
|
+
`;
|
|
598
|
+
}
|