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.
Files changed (127) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/README.md +96 -461
  3. package/package.json +12 -5
  4. package/readme-international/README.de.md +42 -9
  5. package/readme-international/README.es.md +43 -10
  6. package/readme-international/README.fr.md +42 -9
  7. package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +5 -3
  8. package/src/bmad-plus/module.yaml +270 -220
  9. package/src/bmad-plus/packs/pack-dev-studio/README.md +133 -141
  10. package/src/bmad-plus/packs/pack-dev-studio/SKILL.md +49 -0
  11. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +35 -60
  12. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +59 -59
  13. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +55 -93
  14. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +58 -93
  15. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +55 -132
  16. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +63 -78
  17. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +54 -69
  18. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +54 -93
  19. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +32 -60
  20. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +67 -71
  21. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +61 -90
  22. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +56 -78
  23. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +55 -88
  24. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +20 -153
  25. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +20 -173
  26. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +14 -220
  27. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +20 -329
  28. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +15 -314
  29. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +15 -355
  30. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +15 -375
  31. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +14 -357
  32. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +13 -78
  33. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +52 -65
  34. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +14 -81
  35. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +14 -31
  36. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +14 -45
  37. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +13 -128
  38. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +61 -87
  39. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +55 -298
  40. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +52 -426
  41. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +48 -69
  42. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +22 -80
  43. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +60 -482
  44. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +50 -184
  45. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +57 -173
  46. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +56 -108
  47. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +54 -1509
  48. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +54 -296
  49. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +41 -283
  50. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +58 -18
  51. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +103 -72
  52. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +55 -27
  53. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +34 -60
  54. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +46 -87
  55. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/steps/step-01-init.md +10 -0
  56. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +30 -60
  57. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +57 -27
  58. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +47 -138
  59. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +48 -34
  60. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +51 -68
  61. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +46 -3
  62. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +68 -109
  63. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +53 -174
  64. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +39 -53
  65. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +45 -83
  66. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +45 -176
  67. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +45 -63
  68. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +53 -124
  69. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +44 -100
  70. package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +56 -116
  71. package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +49 -12
  72. package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +51 -328
  73. package/src/bmad-plus/packs/pack-dev-studio/shared/catalog.json +489 -0
  74. package/src/bmad-plus/packs/pack-dev-studio/shared/execution.md +69 -0
  75. package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +39 -32
  76. package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +85 -14
  77. package/src/bmad-plus/packs/pack-memory/README.md +6 -0
  78. package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +12 -5
  79. package/src/bmad-plus/packs/pack-seo/SKILL.md +27 -1
  80. package/src/bmad-plus/packs/pack-seo/scripts/seo_apis.py +8 -8
  81. package/src/bmad-plus/packs/pack-seo/scripts/seo_fetch.py +1 -2
  82. package/src/bmad-plus/packs/pack-seo/scripts/seo_report.py +0 -1
  83. package/src/bmad-plus/packs/pack-seo/seo-chief.md +16 -1
  84. package/src/bmad-plus/packs/pack-seo/seo-judge.md +12 -0
  85. package/src/bmad-plus/packs/pack-seo/seo-scout.md +12 -0
  86. package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +4 -4
  87. package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +5 -3
  88. package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
  89. package/tools/bmad-plus-npx.js +4 -2
  90. package/tools/build/adapters.config.js +60 -51
  91. package/tools/build/check-counts.js +52 -54
  92. package/tools/build/check-install-contract.js +450 -0
  93. package/tools/build/generate-adapters.js +252 -56
  94. package/tools/build/generate.js +364 -14
  95. package/tools/build/generated-adapters/.codex/AGENTS.md +20 -7
  96. package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +20 -7
  97. package/tools/build/generated-adapters/.opencode/AGENTS.md +20 -7
  98. package/tools/build/generated-adapters/AGENTS.md +20 -7
  99. package/tools/build/generated-adapters/CLAUDE.md +20 -7
  100. package/tools/build/generated-adapters/CONVENTIONS.md +20 -7
  101. package/tools/build/generated-adapters/GEMINI.md +20 -7
  102. package/tools/build/module.template.yaml +82 -0
  103. package/tools/build/pack-delivery.js +78 -0
  104. package/tools/cli/bmad-plus-cli.js +16 -1
  105. package/tools/cli/commands/doctor.js +49 -217
  106. package/tools/cli/commands/install.js +109 -164
  107. package/tools/cli/commands/studio.js +68 -0
  108. package/tools/cli/commands/uninstall.js +173 -65
  109. package/tools/cli/commands/update-check.js +31 -0
  110. package/tools/cli/commands/update-policy.js +39 -0
  111. package/tools/cli/commands/update.js +102 -113
  112. package/tools/cli/i18n.js +60 -0
  113. package/tools/cli/lib/ide-config.js +4 -261
  114. package/tools/cli/lib/install-manifest.js +17 -0
  115. package/tools/cli/lib/installation-health.js +360 -0
  116. package/tools/cli/lib/installed-adapters.js +89 -0
  117. package/tools/cli/lib/npm-runner.js +177 -0
  118. package/tools/cli/lib/pack-copy.js +62 -66
  119. package/tools/cli/lib/packs.js +546 -3
  120. package/tools/cli/lib/python-health.js +233 -0
  121. package/tools/cli/lib/python-provision.js +2 -2
  122. package/tools/cli/lib/studio.js +310 -0
  123. package/tools/cli/lib/update-check.js +153 -0
  124. package/tools/cli/lib/update-dispatch.js +182 -0
  125. package/tools/cli/lib/update-policy.js +90 -0
  126. package/tools/cli/lib/update-transaction.js +334 -0
  127. package/tools/release/publication-content.js +901 -0
@@ -3,17 +3,19 @@
3
3
  * BMAD+ Build — registry.yaml → pack artifacts generator (Pillar 1)
4
4
  *
5
5
  * Reads the root registry.yaml (the single source of truth) and generates the
6
- * pack data currently hand-maintained in tools/cli/lib/packs.js:
6
+ * pack data in tools/cli/lib/packs.js and src/bmad-plus/module.yaml:
7
7
  * - PACKS (CLI pack definitions)
8
8
  * - PACK_ORDER (install/display order)
9
9
  * - EXPECTED_AGENTS (what `bmad-plus doctor` verifies after install)
10
+ * - DERIVED (counts, display metadata, targets, Python provisioning)
10
11
  *
11
12
  * Usage:
12
13
  * node tools/build/generate.js # print generated packs module source
13
- * node tools/build/generate.js --json # print { PACKS, PACK_ORDER, EXPECTED_AGENTS } as JSON
14
+ * node tools/build/generate.js --json # print all generated data as JSON
14
15
  * node tools/build/generate.js --out <file> # write generated module source to <file>
16
+ * --module-out <yaml> # also write generated module.yaml
15
17
  * node tools/build/generate.js --check # verify registry.yaml reproduces the live
16
- * # tools/cli/lib/packs.js exactly (exit 1 on drift)
18
+ * # packs.js and module.yaml (exit 1 on drift)
17
19
  *
18
20
  * Author: Laurent Rochetta
19
21
  */
@@ -28,9 +30,65 @@ const yaml = require('js-yaml');
28
30
  const REPO_ROOT = path.join(__dirname, '..', '..');
29
31
  const DEFAULT_REGISTRY_PATH = path.join(REPO_ROOT, 'registry.yaml');
30
32
  const DEFAULT_PACKS_MODULE_PATH = path.join(REPO_ROOT, 'tools', 'cli', 'lib', 'packs.js');
33
+ const DEFAULT_MODULE_PATH = path.join(REPO_ROOT, 'src', 'bmad-plus', 'module.yaml');
34
+ const MODULE_TEMPLATE_PATH = path.join(__dirname, 'module.template.yaml');
31
35
  const PACKAGE_JSON_PATH = path.join(REPO_ROOT, 'package.json');
32
36
 
33
37
  const INSTALL_LAYOUTS = ['loose', 'packaged'];
38
+ const OWNED_COUNT_RE =
39
+ /(?<![\w.-])\d+\+?[\s-]+(?:[A-Za-zÀ-ÿ-]+\s+){0,6}(?:agents?|skills?|workflows?|packs?|frameworks?|languages?|reference files)\b/gi;
40
+ const STANDARD_PREFIX_RE =
41
+ /\b(?:ISO(?:\/IEC)?|IEC|SOC|NIST(?: SP)?|PCI(?: DSS)?|WCAG|Section)\s*$/i;
42
+
43
+ function hasOwnedCount(value) {
44
+ return [...value.matchAll(OWNED_COUNT_RE)].some(
45
+ (match) => !STANDARD_PREFIX_RE.test(value.slice(0, match.index))
46
+ );
47
+ }
48
+
49
+ function assertResourcePath(file, where) {
50
+ if (
51
+ typeof file !== 'string' ||
52
+ !file ||
53
+ /[\\:\0]/.test(file) ||
54
+ file.split('/').some((part) => !part || part === '.' || part === '..')
55
+ ) {
56
+ throw new Error(`${where}: required resource must be a portable relative file path`);
57
+ }
58
+ }
59
+
60
+ /** Installed entry points, derived from the same layout used by pack-copy.js. */
61
+ function buildRequiredResources(pack) {
62
+ const where = `registry.yaml: pack "${pack.pack_dir}"`;
63
+ for (const file of [
64
+ ...pack.agents,
65
+ ...(pack.skills || []),
66
+ ...(pack.data || []),
67
+ ...((pack.doctor || {}).pack_agents || []),
68
+ ...((pack.doctor || {}).required_resources || []),
69
+ ...(pack.python_entry ? [pack.python_entry] : []),
70
+ ]) {
71
+ assertResourcePath(file, where);
72
+ }
73
+ const resources = [];
74
+ if (pack.install_layout === 'loose') {
75
+ resources.push(...pack.agents.map((id) => `.agents/skills/${id}/SKILL.md`));
76
+ } else {
77
+ resources.push(
78
+ ...pack.doctor.pack_agents.map((file) => `.agents/skills/${pack.pack_dir}/${file}`)
79
+ );
80
+ }
81
+ resources.push(...(pack.skills || []).map((id) => `.agents/skills/${id}/SKILL.md`));
82
+ resources.push(...(pack.data || []).map((file) => `.agents/data/${file}`));
83
+ resources.push(
84
+ ...((pack.doctor || {}).required_resources || []).map(
85
+ (file) => `.agents/skills/${pack.pack_dir}/${file}`
86
+ )
87
+ );
88
+ if (pack.python_entry) resources.push(`.agents/skills/${pack.pack_dir}/${pack.python_entry}`);
89
+ for (const file of resources) assertResourcePath(file, where);
90
+ return [...new Set(resources)];
91
+ }
34
92
 
35
93
  /**
36
94
  * Validate the minimal contract the generator relies on.
@@ -53,11 +111,26 @@ function validateRegistry(registry) {
53
111
  seenOrders.add(pack.order);
54
112
 
55
113
  if (!pack.cli || typeof pack.cli !== 'object') throw new Error(`${where}: missing "cli" block`);
56
- for (const field of ['name', 'icon', 'desc']) {
114
+ for (const field of ['name', 'icon']) {
57
115
  if (typeof pack.cli[field] !== 'string' || pack.cli[field] === '') {
58
116
  throw new Error(`${where}: missing cli.${field}`);
59
117
  }
60
118
  }
119
+ if (!pack.cli.desc && !pack.cli.desc_template) {
120
+ throw new Error(`${where}: missing cli.desc or cli.desc_template`);
121
+ }
122
+ for (const [field, value] of Object.entries({
123
+ 'cli.desc': pack.cli.desc,
124
+ 'cli.desc_template': pack.cli.desc_template,
125
+ summary: pack.summary,
126
+ summary_template: pack.summary_template,
127
+ })) {
128
+ if (value !== undefined && (typeof value !== 'string' || hasOwnedCount(value))) {
129
+ throw new Error(
130
+ `${where}: ${field} must use derived placeholders for owned count dimensions`
131
+ );
132
+ }
133
+ }
61
134
  if (!Array.isArray(pack.agents)) throw new Error(`${where}: "agents" must be a list`);
62
135
  if (pack.skills !== undefined && !Array.isArray(pack.skills)) {
63
136
  throw new Error(`${where}: "skills" must be a list`);
@@ -71,6 +144,66 @@ function validateRegistry(registry) {
71
144
  if (pack.install_layout === 'packaged' && !Array.isArray((pack.doctor || {}).pack_agents)) {
72
145
  throw new Error(`${where}: packaged layout requires "doctor.pack_agents" list`);
73
146
  }
147
+ if (
148
+ pack.doctor?.required_resources !== undefined &&
149
+ !Array.isArray(pack.doctor.required_resources)
150
+ ) {
151
+ throw new Error(`${where}: doctor.required_resources must be a list`);
152
+ }
153
+ if (
154
+ !Array.isArray(pack.runtime) ||
155
+ pack.runtime.length === 0 ||
156
+ pack.runtime.some((runtime) => !Object.hasOwn(registry.runtimes || {}, runtime))
157
+ ) {
158
+ throw new Error(`${where}: runtime must declare known runtimes`);
159
+ }
160
+ buildRequiredResources(pack);
161
+ if (pack.personas !== undefined) {
162
+ if (!Array.isArray(pack.personas)) throw new Error(`${where}: personas must be a list`);
163
+ const roster = new Set([...pack.agents, ...(pack.sub_agents || [])]);
164
+ const ids = new Set();
165
+ for (const persona of pack.personas) {
166
+ if (
167
+ !persona ||
168
+ ['id', 'name', 'role', 'description'].some(
169
+ (key) => typeof persona[key] !== 'string' || !persona[key].trim()
170
+ )
171
+ ) {
172
+ throw new Error(`${where}: each persona needs id, name, role and description`);
173
+ }
174
+ if (!roster.has(persona.id) || ids.has(persona.id)) {
175
+ throw new Error(
176
+ `${where}: persona ${persona.id} is absent from its roster or duplicated`
177
+ );
178
+ }
179
+ ids.add(persona.id);
180
+ }
181
+ }
182
+ }
183
+ const integration = registry.targets?.integration;
184
+ if (
185
+ integration?.execution !== 'host-managed' ||
186
+ integration?.lifecycle_events !== 'not-integrated'
187
+ ) {
188
+ throw new Error(
189
+ 'registry.yaml: targets.integration must declare the delivered instruction integration'
190
+ );
191
+ }
192
+ for (const [runtime, config] of Object.entries(registry.runtimes || {})) {
193
+ if (
194
+ typeof config?.min_version !== 'string' ||
195
+ !/^\d+\.\d+(?:\.\d+)?$/.test(config.min_version)
196
+ ) {
197
+ throw new Error(`registry.yaml: runtime "${runtime}" needs a numeric min_version`);
198
+ }
199
+ }
200
+ const tools = new Set();
201
+ for (const adapter of registry.targets.adapters || []) {
202
+ if (!adapter || typeof adapter.tool !== 'string' || !adapter.tool || tools.has(adapter.tool)) {
203
+ throw new Error('registry.yaml: adapter tool names must be nonempty and unique');
204
+ }
205
+ tools.add(adapter.tool);
206
+ assertResourcePath(adapter.file, `registry.yaml: adapter "${adapter.tool}"`);
74
207
  }
75
208
  return registry;
76
209
  }
@@ -99,6 +232,7 @@ function buildPackOrder(registry) {
99
232
  * - `required` only when true (other packs omit the key entirely)
100
233
  */
101
234
  function buildPacks(registry) {
235
+ const derived = buildDerived(registry);
102
236
  const packs = {};
103
237
  for (const [id, p] of sortedPackEntries(registry)) {
104
238
  const entry = {
@@ -112,12 +246,192 @@ function buildPacks(registry) {
112
246
  entry.packDir = p.pack_dir;
113
247
  entry.packSrcDir = p.pack_src_dir || 'packs';
114
248
  if (p.required === true) entry.required = true;
115
- entry.desc = p.cli.desc;
249
+ entry.desc = derived.packs[id].desc;
116
250
  packs[id] = entry;
117
251
  }
118
252
  return packs;
119
253
  }
120
254
 
255
+ /** Recursively count reference files; sourceRoot is injectable for fixture trees. */
256
+ function countMarkdownFiles(dir) {
257
+ if (!fs.existsSync(dir)) return 0;
258
+ return fs.readdirSync(dir, { withFileTypes: true }).reduce((count, entry) => {
259
+ if (entry.isDirectory()) return count + countMarkdownFiles(path.join(dir, entry.name));
260
+ return count + Number(entry.isFile() && entry.name.endsWith('.md'));
261
+ }, 0);
262
+ }
263
+
264
+ function renderSummary(pack, derived, field = 'summary') {
265
+ const template = field === 'summary' ? pack.summary_template : pack.cli.desc_template;
266
+ const fallback = field === 'summary' ? pack.summary : pack.cli.desc;
267
+ if (!template) return fallback || '';
268
+ const values = {
269
+ agent_count: derived.agentCount,
270
+ installer_agent_count: derived.installerAgentCount,
271
+ sub_agent_count: derived.subAgentCount,
272
+ workflow_count: derived.workflowCount,
273
+ framework_count: derived.frameworkCount,
274
+ category_count: derived.categoryCount,
275
+ skill_count: derived.skillCount,
276
+ reference_file_count: derived.referenceFiles,
277
+ };
278
+ return template.replace(/\{([a-z_]+)\}/g, (_match, key) => {
279
+ if (!(key in values)) throw new Error(`Unknown derived summary placeholder: ${key}`);
280
+ return String(values[key]);
281
+ });
282
+ }
283
+
284
+ /** The one derivation engine; its JSON result is shipped for runtime consumers. */
285
+ function buildDerived(registry, { sourceRoot = path.join(REPO_ROOT, 'src', 'bmad-plus') } = {}) {
286
+ const packs = {};
287
+ const pythonPacks = {};
288
+ let installerAgents = 0;
289
+ let totalAgents = 0;
290
+ for (const [id, pack] of sortedPackEntries(registry)) {
291
+ const categories = pack.categories || [];
292
+ const categoryAgentCounts = categories.map((category) => (category.agents || []).length);
293
+ const categoryAgentCount = categoryAgentCounts.reduce((sum, count) => sum + count, 0);
294
+ const installerAgentCount = pack.agents.length;
295
+ const subAgentCount = (pack.sub_agents || []).length;
296
+ const facts = {
297
+ id,
298
+ order: pack.order,
299
+ name: pack.cli.name,
300
+ displayName: pack.display_name || pack.cli.name,
301
+ required: pack.required === true,
302
+ installerAgentCount,
303
+ categoryAgentCount,
304
+ agentCount: categoryAgentCount || subAgentCount || installerAgentCount,
305
+ subAgentCount,
306
+ workflowCount:
307
+ (pack.workflows || []).length +
308
+ categories.reduce((sum, c) => sum + (c.workflows || []).length, 0),
309
+ frameworkCount: (pack.compliance_tags || []).length,
310
+ categoryCount: categories.length,
311
+ skillCount: (pack.skills || []).length,
312
+ referenceFiles: countMarkdownFiles(
313
+ path.join(sourceRoot, pack.pack_src_dir || 'packs', pack.pack_dir, 'references')
314
+ ),
315
+ categoryAgentCounts,
316
+ runtimes: [...pack.runtime],
317
+ requiredResources: buildRequiredResources(pack),
318
+ personas: (pack.personas || []).map(({ id, name, role, description, alias }) => ({
319
+ id,
320
+ name,
321
+ role,
322
+ description,
323
+ ...(alias ? { alias } : {}),
324
+ })),
325
+ };
326
+ facts.summary = renderSummary(pack, facts);
327
+ facts.desc = renderSummary(pack, facts, 'description');
328
+ facts.description = facts.desc;
329
+ packs[id] = facts;
330
+ installerAgents += installerAgentCount;
331
+ totalAgents += new Set([
332
+ ...pack.agents,
333
+ ...(pack.sub_agents || []),
334
+ ...categories.flatMap((c) => c.agents || []),
335
+ ]).size;
336
+ if ((pack.runtime || []).includes('python')) {
337
+ const packagePath =
338
+ pack.python_package || `src/bmad-plus/${pack.pack_src_dir || 'packs'}/${pack.pack_dir}`;
339
+ pythonPacks[id] = {
340
+ requirements: [...packagePath.split(/[\\/]/), 'requirements.txt'],
341
+ verifyModules: [...(pack.python_verify_modules || [])],
342
+ };
343
+ }
344
+ }
345
+ const { LANGUAGES } = require('../cli/i18n');
346
+ return {
347
+ product: {
348
+ code: registry.product.code,
349
+ displayName: registry.product.display_name,
350
+ version: registry.product.version,
351
+ derivedFrom: registry.product.derived_from,
352
+ },
353
+ packOrder: buildPackOrder(registry),
354
+ packCount: Object.keys(packs).length,
355
+ installerAgents,
356
+ totalAgents,
357
+ languages: Object.keys(LANGUAGES),
358
+ packs,
359
+ pythonPacks,
360
+ diagnostics: {
361
+ schemaVersion: 1,
362
+ runtimeMinimums: Object.fromEntries(
363
+ Object.entries(registry.runtimes).map(([id, runtime]) => [id, runtime.min_version])
364
+ ),
365
+ integrations: Object.fromEntries(
366
+ registry.targets.adapters.map(({ tool, file }) => [
367
+ tool,
368
+ {
369
+ instructionFile: file,
370
+ execution: registry.targets.integration.execution,
371
+ lifecycleEvents: registry.targets.integration.lifecycle_events,
372
+ },
373
+ ])
374
+ ),
375
+ },
376
+ targets: {
377
+ spine: registry.targets.spine,
378
+ adapters: registry.targets.adapters.map(({ tool, file }) => ({ tool, file })),
379
+ models_supported: [...registry.targets.models_supported],
380
+ },
381
+ };
382
+ }
383
+
384
+ /** Preserve project questions/compatibility separately from generated pack facts. */
385
+ function generateModuleSource(registry, templatePath = MODULE_TEMPLATE_PATH) {
386
+ const moduleConfig = yaml.load(fs.readFileSync(templatePath, 'utf8'));
387
+ const derived = buildDerived(registry);
388
+ moduleConfig.code = registry.product.code;
389
+ moduleConfig.packs = {};
390
+ for (const [id, pack] of sortedPackEntries(registry)) {
391
+ const facts = derived.packs[id];
392
+ const entry = {
393
+ name: facts.displayName,
394
+ icon: pack.icon_emoji,
395
+ description: facts.summary || facts.desc,
396
+ required: facts.required,
397
+ agents: [...pack.agents],
398
+ skills: [...(pack.skills || [])],
399
+ };
400
+ for (const key of [
401
+ 'data',
402
+ 'external_package',
403
+ 'orchestrator',
404
+ 'categories',
405
+ 'workflows',
406
+ 'sub_agents',
407
+ 'required_keys',
408
+ 'optional_keys',
409
+ 'cohabitation_warning',
410
+ ]) {
411
+ if (key in pack) entry[key] = JSON.parse(JSON.stringify(pack[key]));
412
+ }
413
+ if (pack.install_layout === 'packaged') {
414
+ entry.packDir = pack.pack_dir;
415
+ entry.packSrcDir = pack.pack_src_dir || 'packs';
416
+ }
417
+ moduleConfig.packs[id] = entry;
418
+ }
419
+ moduleConfig.install_packs['multi-select'] = [
420
+ ...derived.packOrder
421
+ .filter((id) => !derived.packs[id].required)
422
+ .map((id) => ({
423
+ value: id,
424
+ label: `${registry.packs[id].icon_emoji} ${derived.packs[id].displayName} — ${derived.packs[id].summary || derived.packs[id].desc}`,
425
+ })),
426
+ { value: 'all', label: 'Tout installer' },
427
+ { value: 'none', label: 'Aucun — Core uniquement' },
428
+ ];
429
+ return (
430
+ '# AUTO-GENERATED from registry.yaml + tools/build/module.template.yaml — DO NOT EDIT.\n' +
431
+ yaml.dump(moduleConfig, { lineWidth: 110, noRefs: true })
432
+ );
433
+ }
434
+
121
435
  /**
122
436
  * Generate EXPECTED_AGENTS (consumed by `bmad-plus doctor`).
123
437
  * - loose packs → agent DIRECTORIES checked under .agents/skills/
@@ -135,12 +449,13 @@ function buildExpectedAgents(registry) {
135
449
  return expected;
136
450
  }
137
451
 
138
- /** Convenience: all three artifacts at once. */
452
+ /** Convenience: all runtime artifacts at once. */
139
453
  function buildAll(registry) {
140
454
  return {
141
455
  PACKS: buildPacks(registry),
142
456
  PACK_ORDER: buildPackOrder(registry),
143
457
  EXPECTED_AGENTS: buildExpectedAgents(registry),
458
+ DERIVED: buildDerived(registry),
144
459
  };
145
460
  }
146
461
 
@@ -149,7 +464,7 @@ function buildAll(registry) {
149
464
  * Evaluating this source yields exports deep-equal to the hand-written module.
150
465
  */
151
466
  function generatePacksModuleSource(registry) {
152
- const { PACKS, PACK_ORDER, EXPECTED_AGENTS } = buildAll(registry);
467
+ const { PACKS, PACK_ORDER, EXPECTED_AGENTS, DERIVED } = buildAll(registry);
153
468
  const j = (value) => JSON.stringify(value, null, 2);
154
469
  return [
155
470
  '/**',
@@ -166,13 +481,17 @@ function generatePacksModuleSource(registry) {
166
481
  '',
167
482
  `const EXPECTED_AGENTS = ${j(EXPECTED_AGENTS)};`,
168
483
  '',
169
- 'module.exports = { PACKS, PACK_ORDER, EXPECTED_AGENTS };',
484
+ `const DERIVED = ${j(DERIVED)};`,
485
+ '',
486
+ 'module.exports = { PACKS, PACK_ORDER, EXPECTED_AGENTS, DERIVED };',
170
487
  '',
171
488
  ].join('\n');
172
489
  }
173
490
 
174
491
  /** Human-readable drill-down for a mismatching keyed object. */
175
492
  function describeDiff(label, generated, current, mismatches) {
493
+ generated = generated || {};
494
+ current = current || {};
176
495
  if (Array.isArray(generated) || Array.isArray(current)) {
177
496
  mismatches.push(
178
497
  `${label}: generated ${JSON.stringify(generated)} != current ${JSON.stringify(current)}`
@@ -202,13 +521,25 @@ function check({
202
521
  registryPath = DEFAULT_REGISTRY_PATH,
203
522
  packsModulePath = DEFAULT_PACKS_MODULE_PATH,
204
523
  packageJsonPath = PACKAGE_JSON_PATH,
524
+ modulePath = DEFAULT_MODULE_PATH,
205
525
  } = {}) {
206
526
  const registry = loadRegistry(registryPath);
527
+ delete require.cache[require.resolve(packsModulePath)];
207
528
  const current = require(packsModulePath);
208
529
  const generated = buildAll(registry);
209
530
  const mismatches = [];
210
531
 
211
532
  const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
533
+ const { checkPythonDelivery } = require('./pack-delivery');
534
+ mismatches.push(...checkPythonDelivery(registry, { packageRoot: REPO_ROOT, packageJson: pkg }));
535
+ try {
536
+ require('../cli/lib/studio').validatePack(
537
+ path.join(REPO_ROOT, 'src/bmad-plus/packs/pack-dev-studio'),
538
+ registry.packs['dev-studio']
539
+ );
540
+ } catch (error) {
541
+ mismatches.push('Dev Studio: ' + error.message);
542
+ }
212
543
  if (!registry.product || registry.product.version !== pkg.version) {
213
544
  const declared = registry.product ? registry.product.version : undefined;
214
545
  mismatches.push(
@@ -216,11 +547,16 @@ function check({
216
547
  );
217
548
  }
218
549
 
219
- for (const key of ['PACKS', 'PACK_ORDER', 'EXPECTED_AGENTS']) {
550
+ for (const key of ['PACKS', 'PACK_ORDER', 'EXPECTED_AGENTS', 'DERIVED']) {
220
551
  if (!isDeepStrictEqual(generated[key], current[key])) {
221
552
  describeDiff(key, generated[key], current[key], mismatches);
222
553
  }
223
554
  }
555
+ if (
556
+ fs.readFileSync(modulePath, 'utf8').replace(/\r\n/g, '\n') !== generateModuleSource(registry)
557
+ ) {
558
+ mismatches.push('module.yaml: generated pack metadata differs from registry.yaml');
559
+ }
224
560
 
225
561
  return { ok: mismatches.length === 0, mismatches };
226
562
  }
@@ -233,7 +569,7 @@ function main(argv) {
233
569
  if (args.includes('--check')) {
234
570
  const result = check();
235
571
  if (result.ok) {
236
- console.log('OK — registry.yaml reproduces tools/cli/lib/packs.js exactly (no drift).');
572
+ console.log('OK — registry.yaml reproduces packs.js and module.yaml (no drift).');
237
573
  return 0;
238
574
  }
239
575
  console.error('DRIFT DETECTED between registry.yaml and tools/cli/lib/packs.js:');
@@ -257,6 +593,15 @@ function main(argv) {
257
593
  return 1;
258
594
  }
259
595
  fs.writeFileSync(path.resolve(outPath), source, 'utf8');
596
+ const moduleOutIdx = args.indexOf('--module-out');
597
+ if (moduleOutIdx !== -1) {
598
+ if (!args[moduleOutIdx + 1]) throw new Error('--module-out requires a file path');
599
+ fs.writeFileSync(
600
+ path.resolve(args[moduleOutIdx + 1]),
601
+ generateModuleSource(registry),
602
+ 'utf8'
603
+ );
604
+ }
260
605
  console.log(`Generated ${path.resolve(outPath)} from registry.yaml`);
261
606
  return 0;
262
607
  }
@@ -265,20 +610,25 @@ function main(argv) {
265
610
  return 0;
266
611
  }
267
612
 
268
- if (require.main === module) {
269
- process.exitCode = main(process.argv);
270
- }
271
-
272
613
  module.exports = {
273
614
  DEFAULT_REGISTRY_PATH,
274
615
  DEFAULT_PACKS_MODULE_PATH,
616
+ DEFAULT_MODULE_PATH,
617
+ MODULE_TEMPLATE_PATH,
275
618
  loadRegistry,
276
619
  validateRegistry,
277
620
  buildPacks,
278
621
  buildPackOrder,
279
622
  buildExpectedAgents,
280
623
  buildAll,
624
+ buildDerived,
625
+ renderSummary,
626
+ generateModuleSource,
281
627
  generatePacksModuleSource,
282
628
  check,
283
629
  main,
284
630
  };
631
+
632
+ if (require.main === module) {
633
+ process.exitCode = main(process.argv);
634
+ }
@@ -28,16 +28,21 @@ To activate an agent, say its name or persona:
28
28
  - **Forge** (Architect-Dev) — Architecture + Development + Documentation
29
29
  - **Sentinel** (Quality) — QA + UX review
30
30
  - **Nexus** (Orchestrator) — Sprint management + Autopilot + Parallel execution
31
- - **Shadow** (OSINT) — Investigation + Scraping + Psychoprofiling (if OSINT pack installed)
32
- - **Maker** (Agent Creator) — Design, build, validate, and package new BMAD+ agents
33
- - **Shield** (GRC) — Compliance agents for GDPR, ISO 27001, SOC 2, HIPAA, EU AI Act, DORA, NIS2 and more (derived counts: see Registry facts below)
31
+ - **Shadow** (OSINT) — Investigation + Scraping + Psychoprofiling (OSINT)
32
+ - **Maker** (Agent Creator) — Design, build, validate, and package new BMAD+ agents (Maker)
33
+ - **Shield** (GRC) — Compliance agents for GDPR, ISO 27001, SOC 2, HIPAA, EU AI Act, DORA, NIS2 and more (Shield)
34
+ - **SEO Scout** (Technical Scanner) — Crawling + technical inspection + performance (SEO)
35
+ - **SEO Chief** (Strategist & Reporter) — Scoring + strategy + reporting (SEO)
36
+ - **SEO Judge** (Content & AI Analyst) — Content quality + structured data + GEO analysis (SEO)
37
+ - **Zecher** (זכר, Memory Guardian) — Memory Archivist — Persistent cross-session memory, consolidation, project scanning, context recall, session handoffs (Memory)
34
38
  - **Miriam** (מרים) — Business Analyst — Strategic analysis, research, product briefs (Dev Studio)
35
39
  - **Huldah** (חולדה) — Technical Writer — Documentation, diagrams, editorial review (Dev Studio)
36
40
  - **Yosef** (יוסף) — Product Manager — PRD, requirements, feature prioritization (Dev Studio)
37
41
  - **Rachel** (רחל) — UX Designer — User experience, wireframes, empathy mapping (Dev Studio)
38
42
  - **Bezalel** (בצלאל) — System Architect — Architecture, ADRs, epics & stories (Dev Studio)
39
43
  - **Oholiab** (אהליאב) — Senior Engineer — TDD, sprint, code review, implementation (Dev Studio)
40
- - **Zecher** (זכר, Memory Guardian) — Memory Archivist — Persistent cross-session memory, consolidation, project scanning, context recall, session handoffs
44
+ - **Backup Manager** (Backup & Restore) — Timestamped backups, restoration, and rotation (Backup)
45
+ - **Animated Website Creator** (Website Builder) — Video-driven scrolling websites (Animated)
41
46
 
42
47
  ## Skills
43
48
 
@@ -98,19 +103,27 @@ Agents MUST follow these behavioral principles:
98
103
  - `.agents/memory/patterns.md` — Write when a reusable pattern is validated
99
104
  - `.agents/memory/context.md` — Update at session end with project state
100
105
 
106
+ ## Framework updates
107
+
108
+ Once per session, check the installed framework before starting project work. If this is the BMAD+ source checkout, use `node tools/cli/bmad-plus-cli.js update-check --json`; otherwise use `npx --no-install bmad-plus update-check --json` from the project root.
109
+ If the command is unavailable or the result is off, unknown, offline or stale, continue the user task. Do not install a CLI just to perform this check or claim that an offline version is current.
110
+ When `updateAvailable` is true and `canAutoApply` is true, run the same CLI with `update --latest --auto`. This follows the project update policy; it does not grant permission to expand that policy.
111
+ When an update is available but automatic application is not eligible, explain the version and reason. An explicit request to update can be fulfilled with `update --latest --yes`; do not change the policy, overwrite customized files, or resolve conflicts without the user request covering that action.
112
+ After a successful update, reread the project spine and relevant installed skills, or start a fresh session if the client cannot reload them. Respect preserved local instructions. Check only once even when several adapters include this section.
113
+
101
114
  ## Registry facts (computed from registry.yaml — never hand-typed)
102
115
 
103
- - Product: BMAD+ v0.12.2 (derived from BMAD-METHOD v6.6.0)
116
+ - Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
104
117
  - Models supported: claude, gpt, gemini, local (model-agnostic by contract)
105
118
  - Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
106
119
  - Installer agents (14 across all packs):
107
120
  - Core (required): 4 installer agents — Core agents & skills
108
121
  - OSINT: 1 installer agent — OSINT & investigation — 2 compliance frameworks
109
122
  - Maker: 1 installer agent — Agent creation toolkit
110
- - Shield: 1 installer agent — GRC compliance (25+ frameworks) — 27 specialized agents, 11 workflows, 26 compliance frameworks
123
+ - Shield: 1 installer agent — GRC compliance (26 frameworks) — 27 specialized agents, 11 workflows, 26 compliance frameworks
111
124
  - SEO: 3 installer agents — SEO audit & optimization — 1 compliance frameworks
112
125
  - Memory: 1 installer agent — Persistent cross-session memory
113
- - Dev Studio: 1 installer agent — SDLC automation (6 agents, 56+ skills) — 6 sub-agents, 38 workflows
126
+ - Dev Studio: 1 installer agent — SDLC automation (6 agents, specialized workflows) — 6 sub-agents, 38 workflows
114
127
  - Backup: 1 installer agent — Backup & restore
115
128
  - Animated: 1 installer agent — Animated website agents
116
129
 
@@ -33,16 +33,21 @@ To activate an agent, say its name or persona:
33
33
  - **Forge** (Architect-Dev) — Architecture + Development + Documentation
34
34
  - **Sentinel** (Quality) — QA + UX review
35
35
  - **Nexus** (Orchestrator) — Sprint management + Autopilot + Parallel execution
36
- - **Shadow** (OSINT) — Investigation + Scraping + Psychoprofiling (if OSINT pack installed)
37
- - **Maker** (Agent Creator) — Design, build, validate, and package new BMAD+ agents
38
- - **Shield** (GRC) — Compliance agents for GDPR, ISO 27001, SOC 2, HIPAA, EU AI Act, DORA, NIS2 and more (derived counts: see Registry facts below)
36
+ - **Shadow** (OSINT) — Investigation + Scraping + Psychoprofiling (OSINT)
37
+ - **Maker** (Agent Creator) — Design, build, validate, and package new BMAD+ agents (Maker)
38
+ - **Shield** (GRC) — Compliance agents for GDPR, ISO 27001, SOC 2, HIPAA, EU AI Act, DORA, NIS2 and more (Shield)
39
+ - **SEO Scout** (Technical Scanner) — Crawling + technical inspection + performance (SEO)
40
+ - **SEO Chief** (Strategist & Reporter) — Scoring + strategy + reporting (SEO)
41
+ - **SEO Judge** (Content & AI Analyst) — Content quality + structured data + GEO analysis (SEO)
42
+ - **Zecher** (זכר, Memory Guardian) — Memory Archivist — Persistent cross-session memory, consolidation, project scanning, context recall, session handoffs (Memory)
39
43
  - **Miriam** (מרים) — Business Analyst — Strategic analysis, research, product briefs (Dev Studio)
40
44
  - **Huldah** (חולדה) — Technical Writer — Documentation, diagrams, editorial review (Dev Studio)
41
45
  - **Yosef** (יוסף) — Product Manager — PRD, requirements, feature prioritization (Dev Studio)
42
46
  - **Rachel** (רחל) — UX Designer — User experience, wireframes, empathy mapping (Dev Studio)
43
47
  - **Bezalel** (בצלאל) — System Architect — Architecture, ADRs, epics & stories (Dev Studio)
44
48
  - **Oholiab** (אהליאב) — Senior Engineer — TDD, sprint, code review, implementation (Dev Studio)
45
- - **Zecher** (זכר, Memory Guardian) — Memory Archivist — Persistent cross-session memory, consolidation, project scanning, context recall, session handoffs
49
+ - **Backup Manager** (Backup & Restore) — Timestamped backups, restoration, and rotation (Backup)
50
+ - **Animated Website Creator** (Website Builder) — Video-driven scrolling websites (Animated)
46
51
 
47
52
  ## Skills
48
53
 
@@ -103,19 +108,27 @@ Agents MUST follow these behavioral principles:
103
108
  - `.agents/memory/patterns.md` — Write when a reusable pattern is validated
104
109
  - `.agents/memory/context.md` — Update at session end with project state
105
110
 
111
+ ## Framework updates
112
+
113
+ Once per session, check the installed framework before starting project work. If this is the BMAD+ source checkout, use `node tools/cli/bmad-plus-cli.js update-check --json`; otherwise use `npx --no-install bmad-plus update-check --json` from the project root.
114
+ If the command is unavailable or the result is off, unknown, offline or stale, continue the user task. Do not install a CLI just to perform this check or claim that an offline version is current.
115
+ When `updateAvailable` is true and `canAutoApply` is true, run the same CLI with `update --latest --auto`. This follows the project update policy; it does not grant permission to expand that policy.
116
+ When an update is available but automatic application is not eligible, explain the version and reason. An explicit request to update can be fulfilled with `update --latest --yes`; do not change the policy, overwrite customized files, or resolve conflicts without the user request covering that action.
117
+ After a successful update, reread the project spine and relevant installed skills, or start a fresh session if the client cannot reload them. Respect preserved local instructions. Check only once even when several adapters include this section.
118
+
106
119
  ## Registry facts (computed from registry.yaml — never hand-typed)
107
120
 
108
- - Product: BMAD+ v0.12.2 (derived from BMAD-METHOD v6.6.0)
121
+ - Product: BMAD+ v0.14.0 (derived from BMAD-METHOD v6.6.0)
109
122
  - Models supported: claude, gpt, gemini, local (model-agnostic by contract)
110
123
  - Packs (9): Core, OSINT, Maker, Shield, SEO, Memory, Dev Studio, Backup, Animated
111
124
  - Installer agents (14 across all packs):
112
125
  - Core (required): 4 installer agents — Core agents & skills
113
126
  - OSINT: 1 installer agent — OSINT & investigation — 2 compliance frameworks
114
127
  - Maker: 1 installer agent — Agent creation toolkit
115
- - Shield: 1 installer agent — GRC compliance (25+ frameworks) — 27 specialized agents, 11 workflows, 26 compliance frameworks
128
+ - Shield: 1 installer agent — GRC compliance (26 frameworks) — 27 specialized agents, 11 workflows, 26 compliance frameworks
116
129
  - SEO: 3 installer agents — SEO audit & optimization — 1 compliance frameworks
117
130
  - Memory: 1 installer agent — Persistent cross-session memory
118
- - Dev Studio: 1 installer agent — SDLC automation (6 agents, 56+ skills) — 6 sub-agents, 38 workflows
131
+ - Dev Studio: 1 installer agent — SDLC automation (6 agents, specialized workflows) — 6 sub-agents, 38 workflows
119
132
  - Backup: 1 installer agent — Backup & restore
120
133
  - Animated: 1 installer agent — Animated website agents
121
134