bmad-plus 0.16.0 → 0.17.1

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 (66) hide show
  1. package/CHANGELOG.md +114 -29
  2. package/README.md +45 -27
  3. package/SECURITY.md +9 -1
  4. package/osint-agent-package/README.md +13 -0
  5. package/osint-agent-package/SETUP_KEYS.md +13 -0
  6. package/package.json +32 -8
  7. package/readme-international/README.de.md +134 -537
  8. package/readme-international/README.es.md +136 -556
  9. package/readme-international/README.fr.md +135 -553
  10. package/src/bmad-plus/agents/agent-shadow/SKILL.md +6 -0
  11. package/src/bmad-plus/packs/pack-memory/README.md +1 -1
  12. package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +1 -1
  13. package/src/bmad-plus/packs/pack-memory/shared/memory-protocol.md +1 -1
  14. package/src/bmad-plus/packs/pack-shield/README.md +2 -0
  15. package/src/bmad-plus/packs/pack-shield/SKILL.md +2 -0
  16. package/src/bmad-plus/packs/pack-shield/shared/audit-report-template.md +6 -0
  17. package/src/bmad-plus/packs/pack-shield/shield-orchestrator.md +4 -0
  18. package/src/bmad-plus/skills/bmad-plus-uat/SKILL.md +18 -4
  19. package/src/bmad-plus/skills/bmad-plus-uat/template/page.html +95 -19
  20. package/src/bmad-plus/skills/bmad-plus-uat/template/strings.json +90 -10
  21. package/src/bmad-plus/skills/bmad-plus-uat/templates/README.md +63 -0
  22. package/src/bmad-plus/skills/bmad-plus-uat/templates/example-uat-spec.json +95 -0
  23. package/src/bmad-plus/skills/bmad-plus-uat/templates/uat-release-gate.mjs +127 -0
  24. package/src/bmad-plus/skills/bmad-plus-uat/templates/uat-spec-present.mjs +77 -0
  25. package/tools/build/adapters.config.js +1 -1
  26. package/tools/build/generate-adapters.js +186 -60
  27. package/tools/build/generate.js +49 -13
  28. package/tools/cli/bmad-plus-cli.js +3 -6
  29. package/tools/cli/commands/autoconfig.js +66 -24
  30. package/tools/cli/commands/install.js +620 -356
  31. package/tools/cli/commands/memory.js +40 -13
  32. package/tools/cli/commands/scan.js +94 -30
  33. package/tools/cli/commands/uat.js +21 -5
  34. package/tools/cli/commands/uninstall.js +65 -16
  35. package/tools/cli/commands/update-check.js +8 -3
  36. package/tools/cli/commands/update-policy.js +14 -5
  37. package/tools/cli/commands/update.js +89 -20
  38. package/tools/cli/i18n.js +128 -66
  39. package/tools/cli/lib/ide-config.js +3 -1
  40. package/tools/cli/lib/install-manifest.js +13 -6
  41. package/tools/cli/lib/installation-health.js +33 -1
  42. package/tools/cli/lib/installed-adapters.js +29 -6
  43. package/tools/cli/lib/memory-init.js +19 -4
  44. package/tools/cli/lib/packs.js +17 -28
  45. package/tools/cli/lib/path-hash.js +5 -1
  46. package/tools/cli/lib/python-provision.js +51 -12
  47. package/tools/cli/lib/stack-detect.js +51 -25
  48. package/tools/cli/lib/uat.js +18 -0
  49. package/tools/cli/lib/update-check.js +132 -34
  50. package/tools/cli/lib/update-dispatch.js +2 -2
  51. package/tools/cli/lib/update-policy.js +57 -19
  52. package/tools/cli/lib/update-transaction.js +439 -85
  53. package/tools/build/README.md +0 -78
  54. package/tools/build/check-counts.js +0 -626
  55. package/tools/build/check-install-contract.js +0 -648
  56. package/tools/build/generated-adapters/.codex/AGENTS.md +0 -134
  57. package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +0 -139
  58. package/tools/build/generated-adapters/.opencode/AGENTS.md +0 -134
  59. package/tools/build/generated-adapters/AGENTS.md +0 -132
  60. package/tools/build/generated-adapters/CLAUDE.md +0 -135
  61. package/tools/build/generated-adapters/CONVENTIONS.md +0 -134
  62. package/tools/build/generated-adapters/GEMINI.md +0 -139
  63. package/tools/build/generated-adapters/README.md +0 -79
  64. package/tools/maintain/upstream-candidate.js +0 -456
  65. package/tools/release/publication-content.js +0 -903
  66. package/tools/release/supply-chain.js +0 -282
@@ -40,15 +40,23 @@ function analyzeStructure(dir) {
40
40
  if (entry.isDirectory()) {
41
41
  const name = entry.name.toLowerCase();
42
42
  if (name === 'src' || name === 'lib' || name === 'app') structure.hasSrc = true;
43
- if (name === 'tests' || name === 'test' || name === '__tests__' || name === 'spec') structure.hasTests = true;
43
+ if (name === 'tests' || name === 'test' || name === '__tests__' || name === 'spec')
44
+ structure.hasTests = true;
44
45
  if (name === 'docs' || name === 'doc' || name === 'documentation') structure.hasDocs = true;
45
- if (name === '.github' || name === '.gitlab-ci' || name === '.circleci') structure.hasCI = true;
46
+ if (name === '.github' || name === '.gitlab-ci' || name === '.circleci')
47
+ structure.hasCI = true;
46
48
  if (name === '.agents' || name === '_bmad') structure.hasBmad = true;
47
- if (!name.startsWith('.') && name !== 'node_modules') structure.directories.push(entry.name);
49
+ if (!name.startsWith('.') && name !== 'node_modules')
50
+ structure.directories.push(entry.name);
48
51
  } else {
49
52
  structure.fileCount++;
50
53
  const name = entry.name;
51
- if (name === 'Dockerfile' || name === 'docker-compose.yml' || name === 'docker-compose.yaml') structure.hasDocker = true;
54
+ if (
55
+ name === 'Dockerfile' ||
56
+ name === 'docker-compose.yml' ||
57
+ name === 'docker-compose.yaml'
58
+ )
59
+ structure.hasDocker = true;
52
60
  if (name === 'LICENSE' || name === 'LICENSE.md') structure.hasLicense = true;
53
61
  if (name === 'README.md' || name === 'readme.md') structure.hasReadme = true;
54
62
  if (name === '.env' || name === '.env.local') structure.hasEnv = true;
@@ -87,7 +95,9 @@ function calculateHealth(structure) {
87
95
  return { pct, checks };
88
96
  }
89
97
 
90
- function recommendPacks(stack, structure, health) {
98
+ // `health` is measured elsewhere and does not change the recommendation: a project
99
+ // with failing checks needs the same packs as a healthy one.
100
+ function recommendPacks(stack, structure) {
91
101
  const packs = ['core', 'memory']; // Always
92
102
  const reasons = {
93
103
  core: 'Essential multi-role agents (Atlas, Forge, Sentinel, Nexus)',
@@ -109,7 +119,10 @@ function recommendPacks(stack, structure, health) {
109
119
  }
110
120
 
111
121
  // SEO — if it looks like a web project
112
- if (stack.framework && ['Next.js', 'Nuxt', 'Angular', 'React', 'Vue.js', 'Svelte'].includes(stack.framework)) {
122
+ if (
123
+ stack.framework &&
124
+ ['Next.js', 'Nuxt', 'Angular', 'React', 'Vue.js', 'Svelte'].includes(stack.framework)
125
+ ) {
113
126
  packs.push('seo');
114
127
  reasons.seo = `${stack.framework} web app detected — SEO audit agents recommended`;
115
128
  }
@@ -121,21 +134,37 @@ function generateRecommendations(stack, structure, health) {
121
134
  const recs = [];
122
135
 
123
136
  if (!structure.hasTests) {
124
- recs.push({ agent: 'Sentinel', action: 'set up a test framework and write initial tests', priority: 'high' });
137
+ recs.push({
138
+ agent: 'Sentinel',
139
+ action: 'set up a test framework and write initial tests',
140
+ priority: 'high',
141
+ });
125
142
  }
126
143
  if (!structure.hasDocs) {
127
- recs.push({ agent: 'Forge', action: 'document the project architecture and API', priority: 'medium' });
144
+ recs.push({
145
+ agent: 'Forge',
146
+ action: 'document the project architecture and API',
147
+ priority: 'medium',
148
+ });
128
149
  }
129
150
  if (!structure.hasCI) {
130
151
  recs.push({ agent: 'Forge', action: 'set up CI/CD pipeline', priority: 'medium' });
131
152
  }
132
153
  if (health.pct < 60) {
133
- recs.push({ agent: 'Sentinel', action: 'review code quality and project health', priority: 'high' });
154
+ recs.push({
155
+ agent: 'Sentinel',
156
+ action: 'review code quality and project health',
157
+ priority: 'high',
158
+ });
134
159
  }
135
160
 
136
161
  // Context-specific
137
162
  if (structure.hasSrc) {
138
- recs.push({ agent: 'Forge', action: 'continue developing the current module', priority: 'normal' });
163
+ recs.push({
164
+ agent: 'Forge',
165
+ action: 'continue developing the current module',
166
+ priority: 'normal',
167
+ });
139
168
  }
140
169
 
141
170
  // Memory
@@ -201,7 +230,11 @@ module.exports = {
201
230
 
202
231
  // Check if directory has content
203
232
  let entries = [];
204
- try { entries = fs.readdirSync(projectDir).filter(e => !e.startsWith('.')); } catch (e) { console.warn('autoconfig: Failed to list directory', e.message); }
233
+ try {
234
+ entries = fs.readdirSync(projectDir).filter((e) => !e.startsWith('.'));
235
+ } catch (e) {
236
+ console.warn('autoconfig: Failed to list directory', e.message);
237
+ }
205
238
 
206
239
  const isExistingProject = entries.length > 0;
207
240
 
@@ -215,7 +248,7 @@ module.exports = {
215
248
  const stack = detectStack(projectDir);
216
249
  const structure = analyzeStructure(projectDir);
217
250
  const health = calculateHealth(structure);
218
- const { packs, reasons } = recommendPacks(stack, structure, health);
251
+ const { packs, reasons } = recommendPacks(stack, structure);
219
252
  const recs = generateRecommendations(stack, structure, health);
220
253
 
221
254
  spinner.stop('Analysis complete');
@@ -226,8 +259,11 @@ module.exports = {
226
259
  clack.log.info('');
227
260
 
228
261
  // Stack
229
- const stackLabel = [stack.framework, stack.language, stack.runtime].filter(Boolean).join(' + ') || 'Unknown';
230
- clack.log.info(` Stack: ${pc.cyan(stackLabel)}${stack.packageManager ? pc.dim(` (${stack.packageManager})`) : ''}`);
262
+ const stackLabel =
263
+ [stack.framework, stack.language, stack.runtime].filter(Boolean).join(' + ') || 'Unknown';
264
+ clack.log.info(
265
+ ` Stack: ${pc.cyan(stackLabel)}${stack.packageManager ? pc.dim(` (${stack.packageManager})`) : ''}`
266
+ );
231
267
 
232
268
  // Structure
233
269
  const structItems = [];
@@ -242,7 +278,8 @@ module.exports = {
242
278
 
243
279
  // Health
244
280
  const healthColor = health.pct >= 80 ? pc.green : health.pct >= 50 ? pc.yellow : pc.red;
245
- const healthBar = '█'.repeat(Math.round(health.pct / 10)) + '░'.repeat(10 - Math.round(health.pct / 10));
281
+ const healthBar =
282
+ '█'.repeat(Math.round(health.pct / 10)) + '░'.repeat(10 - Math.round(health.pct / 10));
246
283
  clack.log.info(` Health: ${healthColor(`${healthBar} ${health.pct}%`)}`);
247
284
 
248
285
  // Existing BMAD+
@@ -250,7 +287,9 @@ module.exports = {
250
287
  clack.log.info(` BMAD+: ${pc.green('✓ already installed')} — will update`);
251
288
  }
252
289
  if (structure.hasIdeConfigs.length > 0) {
253
- clack.log.info(` IDE configs: ${pc.green('✓')} ${structure.hasIdeConfigs.length} found — will preserve`);
290
+ clack.log.info(
291
+ ` IDE configs: ${pc.green('✓')} ${structure.hasIdeConfigs.length} found — will preserve`
292
+ );
254
293
  }
255
294
 
256
295
  clack.log.info('');
@@ -259,7 +298,9 @@ module.exports = {
259
298
  clack.log.info(pc.bold('📦 Recommended Packs'));
260
299
  clack.log.info('');
261
300
  for (const packId of packs) {
262
- clack.log.info(` ${pc.green('✓')} ${pc.bold(packId.padEnd(12))} ${pc.dim(reasons[packId])}`);
301
+ clack.log.info(
302
+ ` ${pc.green('✓')} ${pc.bold(packId.padEnd(12))} ${pc.dim(reasons[packId])}`
303
+ );
263
304
  }
264
305
  clack.log.info('');
265
306
 
@@ -292,7 +333,7 @@ module.exports = {
292
333
  } else {
293
334
  const detectedIDEs = [];
294
335
  for (const [id, ide] of Object.entries(IDE_CONFIGS)) {
295
- if (ide.detect.some(marker => fs.existsSync(path.join(projectDir, marker)))) {
336
+ if (ide.detect.some((marker) => fs.existsSync(path.join(projectDir, marker)))) {
296
337
  detectedIDEs.push(id);
297
338
  }
298
339
  }
@@ -349,15 +390,15 @@ module.exports = {
349
390
  '## Health',
350
391
  '',
351
392
  `- **Score:** ${health.pct}%`,
352
- ...health.checks.map(c => `- ${c.pass ? '✅' : '❌'} ${c.name}`),
393
+ ...health.checks.map((c) => `- ${c.pass ? '✅' : '❌'} ${c.name}`),
353
394
  '',
354
395
  '## Key Directories',
355
396
  '',
356
- ...structure.directories.slice(0, 15).map(d => `- \`${d}/\``),
397
+ ...structure.directories.slice(0, 15).map((d) => `- \`${d}/\``),
357
398
  '',
358
399
  '## Installed Packs',
359
400
  '',
360
- ...packs.map(p => `- ${p}`),
401
+ ...packs.map((p) => `- ${p}`),
361
402
  '',
362
403
  ];
363
404
 
@@ -371,11 +412,12 @@ module.exports = {
371
412
 
372
413
  const priorityIcon = { high: pc.red('‼'), medium: pc.yellow('!'), normal: pc.dim('·') };
373
414
  for (const rec of recs.slice(0, 5)) {
374
- clack.log.info(` ${priorityIcon[rec.priority] || '·'} "${pc.cyan(rec.agent)}, ${rec.action}"`);
415
+ clack.log.info(
416
+ ` ${priorityIcon[rec.priority] || '·'} "${pc.cyan(rec.agent)}, ${rec.action}"`
417
+ );
375
418
  }
376
419
 
377
420
  clack.log.info('');
378
-
379
421
  } else {
380
422
  // ── MODE B: New Project ──
381
423
  clack.log.info(pc.bold('🆕 Empty directory — starting new project wizard'));
@@ -437,7 +479,7 @@ module.exports = {
437
479
  '',
438
480
  '## Installed Packs',
439
481
  '',
440
- ...packs.map(p => `- ${p}`),
482
+ ...packs.map((p) => `- ${p}`),
441
483
  '',
442
484
  ];
443
485