bmad-plus 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +4 -2
  3. package/package.json +5 -4
  4. package/readme-international/README.de.md +1 -1
  5. package/readme-international/README.es.md +1 -1
  6. package/readme-international/README.fr.md +1 -1
  7. package/src/bmad-plus/agents/pack-animated/animated-website-agent.md +325 -0
  8. package/src/bmad-plus/agents/pack-animated/templates/animated-website-workflow.md +55 -0
  9. package/src/bmad-plus/agents/pack-backup/backup-agent.md +71 -0
  10. package/src/bmad-plus/agents/pack-backup/templates/backup-workflow.md +51 -0
  11. package/src/bmad-plus/agents/pack-seo/SKILL.md +171 -0
  12. package/src/bmad-plus/agents/pack-seo/checklist.md +140 -0
  13. package/src/bmad-plus/agents/pack-seo/pagespeed-playbook.md +320 -0
  14. package/src/bmad-plus/agents/pack-seo/ref/audit-schema.json +187 -0
  15. package/src/bmad-plus/agents/pack-seo/ref/cwv-thresholds.md +87 -0
  16. package/src/bmad-plus/agents/pack-seo/ref/eeat-criteria.md +123 -0
  17. package/src/bmad-plus/agents/pack-seo/ref/geo-signals.md +167 -0
  18. package/src/bmad-plus/agents/pack-seo/ref/hreflang-rules.md +153 -0
  19. package/src/bmad-plus/agents/pack-seo/ref/quality-gates.md +133 -0
  20. package/src/bmad-plus/agents/pack-seo/ref/schema-catalog.md +91 -0
  21. package/src/bmad-plus/agents/pack-seo/ref/schema-templates.json +356 -0
  22. package/src/bmad-plus/agents/pack-seo/seo-chief.md +294 -0
  23. package/src/bmad-plus/agents/pack-seo/seo-judge.md +241 -0
  24. package/src/bmad-plus/agents/pack-seo/seo-scout.md +171 -0
  25. package/src/bmad-plus/agents/pack-seo/templates/seo-audit-workflow.md +241 -0
  26. package/src/bmad-plus/module.yaml +29 -0
  27. package/tools/cli/bmad-plus-cli.js +23 -0
  28. package/tools/cli/commands/doctor.js +175 -0
  29. package/tools/cli/commands/install.js +54 -16
  30. package/tools/cli/commands/uninstall.js +34 -8
  31. package/tools/cli/commands/update.js +172 -0
  32. package/tools/cli/i18n.js +425 -303
@@ -2,6 +2,7 @@
2
2
 
3
3
  /**
4
4
  * BMAD+ CLI — Main entry point
5
+ * Commands: install, uninstall, update, doctor
5
6
  */
6
7
 
7
8
  const { program } = require('commander');
@@ -22,6 +23,8 @@ if (process.stdin.isTTY) {
22
23
  // Register commands
23
24
  const install = require('./commands/install');
24
25
  const uninstall = require('./commands/uninstall');
26
+ const update = require('./commands/update');
27
+ const doctor = require('./commands/doctor');
25
28
 
26
29
  program
27
30
  .version(packageJson.version)
@@ -43,6 +46,26 @@ const uninstallCmd = program
43
46
  .description('Remove BMAD+ from your project');
44
47
  uninstallCmd.action(uninstall.action);
45
48
 
49
+ // Update command
50
+ const updateCmd = program
51
+ .command('update')
52
+ .description('Update BMAD+ agents and skills (preserves config)');
53
+
54
+ for (const option of update.options || []) {
55
+ updateCmd.option(...option);
56
+ }
57
+ updateCmd.action(update.action);
58
+
59
+ // Doctor command
60
+ const doctorCmd = program
61
+ .command('doctor')
62
+ .description('Check BMAD+ installation integrity');
63
+
64
+ for (const option of doctor.options || []) {
65
+ doctorCmd.option(...option);
66
+ }
67
+ doctorCmd.action(doctor.action);
68
+
46
69
  program.parse(process.argv);
47
70
 
48
71
  if (process.argv.slice(2).length === 0) {
@@ -0,0 +1,175 @@
1
+ /**
2
+ * BMAD+ Doctor Command
3
+ * Checks installation integrity and reports issues
4
+ *
5
+ * Author: Laurent Rochetta
6
+ */
7
+
8
+ const path = require('node:path');
9
+ const fs = require('node:fs');
10
+ const clack = require('@clack/prompts');
11
+ const pc = require('picocolors');
12
+ const { t } = require('../i18n');
13
+
14
+ module.exports = {
15
+ command: 'doctor',
16
+ description: 'Check BMAD+ installation integrity',
17
+ options: [
18
+ ['-d, --directory <path>', 'Project directory (default: current directory)'],
19
+ ],
20
+ action: async (options) => {
21
+ const projectDir = path.resolve(options.directory || process.cwd());
22
+ const packageJson = require('../../../package.json');
23
+
24
+ clack.intro(pc.bgBlue(pc.white(` BMAD+ Doctor v${packageJson.version} `)));
25
+
26
+ let checks = 0;
27
+ let passed = 0;
28
+ let warnings = 0;
29
+ let errors = 0;
30
+
31
+ // ── Check 1: Installation manifest ──
32
+ checks++;
33
+ const manifestPath = path.join(projectDir, '_bmad', '.bmad-plus-install.json');
34
+ if (!fs.existsSync(manifestPath)) {
35
+ clack.log.error('❌ BMAD+ is not installed in this directory');
36
+ clack.outro(pc.red('Run `npx bmad-plus install` first.'));
37
+ return;
38
+ }
39
+
40
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
41
+ clack.log.success(`✅ Manifest found — v${manifest.version}`);
42
+ passed++;
43
+
44
+ // ── Check 2: Version comparison ──
45
+ checks++;
46
+ if (manifest.version === packageJson.version) {
47
+ clack.log.success(`✅ Version up to date (v${manifest.version})`);
48
+ passed++;
49
+ } else {
50
+ clack.log.warn(`⚠️ Version mismatch: installed v${manifest.version} → latest v${packageJson.version}`);
51
+ clack.log.info(' Run `npx bmad-plus update` to upgrade');
52
+ warnings++;
53
+ }
54
+
55
+ // ── Check 3: Agent files ──
56
+ checks++;
57
+ const agentsDir = path.join(projectDir, '.agents', 'skills');
58
+ if (fs.existsSync(agentsDir)) {
59
+ const agentDirs = fs.readdirSync(agentsDir, { withFileTypes: true })
60
+ .filter(d => d.isDirectory())
61
+ .map(d => d.name);
62
+ clack.log.success(`✅ ${agentDirs.length} agent/skill directories found`);
63
+ passed++;
64
+
65
+ // Check each expected agent from manifest packs
66
+ const expectedAgents = {
67
+ core: ['agent-strategist', 'agent-architect-dev', 'agent-quality', 'agent-orchestrator'],
68
+ osint: ['agent-shadow'],
69
+ maker: ['agent-maker'],
70
+ seo: ['pack-seo'],
71
+ backup: ['pack-backup'],
72
+ animated: ['pack-animated'],
73
+ };
74
+
75
+ for (const pack of (manifest.packs || ['core'])) {
76
+ const expected = expectedAgents[pack] || [];
77
+ for (const agent of expected) {
78
+ checks++;
79
+ const agentPath = path.join(agentsDir, agent);
80
+ if (fs.existsSync(agentPath)) {
81
+ passed++;
82
+ } else {
83
+ clack.log.warn(`⚠️ Missing agent: ${agent} (pack: ${pack})`);
84
+ warnings++;
85
+ }
86
+ }
87
+ }
88
+ } else {
89
+ clack.log.error('❌ No .agents/skills/ directory found');
90
+ errors++;
91
+ }
92
+
93
+ // ── Check 4: Config files ──
94
+ checks++;
95
+ const configPath = path.join(projectDir, '_bmad', 'config.yaml');
96
+ if (fs.existsSync(configPath)) {
97
+ clack.log.success('✅ config.yaml present');
98
+ passed++;
99
+ } else {
100
+ clack.log.error('❌ config.yaml missing');
101
+ errors++;
102
+ }
103
+
104
+ // ── Check 5: Module config ──
105
+ checks++;
106
+ const moduleYaml = path.join(projectDir, '_bmad', 'module.yaml');
107
+ if (fs.existsSync(moduleYaml)) {
108
+ clack.log.success('✅ module.yaml present');
109
+ passed++;
110
+ } else {
111
+ clack.log.warn('⚠️ module.yaml missing');
112
+ warnings++;
113
+ }
114
+
115
+ // ── Check 6: IDE configs ──
116
+ checks++;
117
+ const ideFiles = ['CLAUDE.md', 'GEMINI.md', 'AGENTS.md'].filter(f =>
118
+ fs.existsSync(path.join(projectDir, f))
119
+ );
120
+ if (ideFiles.length > 0) {
121
+ clack.log.success(`✅ ${ideFiles.length} IDE config(s): ${ideFiles.join(', ')}`);
122
+ passed++;
123
+ } else {
124
+ clack.log.warn('⚠️ No IDE config files found');
125
+ warnings++;
126
+ }
127
+
128
+ // ── Check 7: Output directories ──
129
+ checks++;
130
+ const outputDir = path.join(projectDir, '_bmad-output');
131
+ if (fs.existsSync(outputDir)) {
132
+ clack.log.success('✅ _bmad-output/ directory exists');
133
+ passed++;
134
+ } else {
135
+ clack.log.warn('⚠️ _bmad-output/ not found');
136
+ warnings++;
137
+ }
138
+
139
+ // ── Check 8: Skills integrity (SKILL.md exists in pack dirs) ──
140
+ if (manifest.packs && manifest.packs.includes('seo')) {
141
+ checks++;
142
+ const seoSkill = path.join(agentsDir, 'pack-seo', 'SKILL.md');
143
+ if (fs.existsSync(seoSkill)) {
144
+ clack.log.success('✅ SEO Engine SKILL.md present');
145
+ passed++;
146
+ } else {
147
+ clack.log.error('❌ SEO Engine SKILL.md missing — /seo audit will not work');
148
+ errors++;
149
+ }
150
+ }
151
+
152
+ // ── Summary ──
153
+ const summaryColor = errors > 0 ? pc.red : warnings > 0 ? pc.yellow : pc.green;
154
+ const summaryIcon = errors > 0 ? '❌' : warnings > 0 ? '⚠️' : '✅';
155
+
156
+ clack.note([
157
+ `${summaryIcon} ${passed}/${checks} checks passed`,
158
+ errors > 0 ? `❌ ${errors} error(s)` : null,
159
+ warnings > 0 ? `⚠️ ${warnings} warning(s)` : null,
160
+ '',
161
+ `📦 Version: v${manifest.version}`,
162
+ `📅 Installed: ${manifest.installed.split('T')[0]}`,
163
+ `🗣️ Language: ${manifest.language || 'N/A'}`,
164
+ `📦 Packs: ${(manifest.packs || ['core']).join(', ')}`,
165
+ ].filter(Boolean).join('\n'), '🩺 BMAD+ Health Report');
166
+
167
+ clack.outro(summaryColor(
168
+ errors > 0
169
+ ? 'Issues found — run `npx bmad-plus install` to fix'
170
+ : warnings > 0
171
+ ? 'Minor issues found — consider running `npx bmad-plus update`'
172
+ : 'Everything looks great! 🎉'
173
+ ));
174
+ },
175
+ };
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * BMAD+ Install Command
3
3
  * Installs agents, skills, and IDE configs into the current project
4
- * Supports 9 languages: EN, FR, ES, DE, PT-BR, RU, ZH, HE, JA
4
+ * Supports 10 languages: EN, FR, ES, DE, PT-BR, RU, ZH, HE, JA, IT
5
5
  *
6
6
  * Author: Laurent Rochetta
7
7
  */
@@ -54,29 +54,29 @@ const PACKS = {
54
54
  seo: {
55
55
  name: 'SEO Audit 360',
56
56
  icon: '🔍',
57
- description: '9-category audit for search engines + AI engines (by Oveanet)',
57
+ description: '3 agents (Scout, Chief, Judge) + 6-phase audit + PageSpeed loop',
58
58
  required: false,
59
59
  agents: [],
60
60
  skills: [],
61
- oveanetAgent: 'seo-audit-360',
61
+ packDir: 'pack-seo',
62
62
  },
63
63
  backup: {
64
64
  name: 'Universal Backup',
65
65
  icon: '🗂️',
66
- description: 'Timestamped ZIP backup with smart exclusions (by Oveanet)',
66
+ description: 'Timestamped ZIP backup with smart exclusions',
67
67
  required: false,
68
68
  agents: [],
69
69
  skills: [],
70
- oveanetAgent: 'universal-backup',
70
+ packDir: 'pack-backup',
71
71
  },
72
72
  animated: {
73
73
  name: 'Animated Website',
74
74
  icon: '🎬',
75
- description: 'Luxury scroll-driven website from video (by Oveanet)',
75
+ description: 'Luxury scroll-driven website from video',
76
76
  required: false,
77
77
  agents: [],
78
78
  skills: [],
79
- oveanetAgent: 'animated-website',
79
+ packDir: 'pack-animated',
80
80
  },
81
81
  };
82
82
 
@@ -118,7 +118,9 @@ module.exports = {
118
118
  const bmadSrc = path.join(__dirname, '..', '..', '..', 'src', 'bmad-plus');
119
119
 
120
120
  // ── Step 0: Language Selection ──
121
- clack.intro(pc.bgCyan(pc.black(' BMAD+ Installer v0.4.1 ')));
121
+ const pkgJson = require('../../../package.json');
122
+ clack.intro(pc.bgCyan(pc.black(` BMAD+ Installer v${pkgJson.version} `)));
123
+ clack.log.info(pc.dim('✨ Created by Laurent Rochetta — github.com/lrochetta'));
122
124
 
123
125
  let lang = 'en';
124
126
  if (!options.yes) {
@@ -316,13 +318,14 @@ module.exports = {
316
318
  }
317
319
  }
318
320
 
319
- // Copy Oveanet agent pack (SEO, Backup, Animated Website)
320
- if (pack.oveanetAgent) {
321
- const oveanetSrc = path.join(__dirname, '..', '..', '..', 'oveanet-pack', pack.oveanetAgent);
322
- const oveanetDest = path.join(targetAgentsDir, pack.oveanetAgent);
323
- if (fs.existsSync(oveanetSrc)) {
324
- fsExtra.copySync(oveanetSrc, oveanetDest, { overwrite: true });
325
- copiedSkills++;
321
+ // Copy pack directory (SEO, Backup, Animated Website)
322
+ if (pack.packDir) {
323
+ const packSrc = path.join(bmadSrc, 'agents', pack.packDir);
324
+ const packDest = path.join(targetAgentsDir, pack.packDir);
325
+ if (fs.existsSync(packSrc)) {
326
+ fsExtra.copySync(packSrc, packDest, { overwrite: true });
327
+ copiedAgents++;
328
+ copiedFiles++;
326
329
  }
327
330
  }
328
331
  }
@@ -372,8 +375,9 @@ module.exports = {
372
375
  fsExtra.ensureDirSync(path.join(projectDir, 'docs'));
373
376
 
374
377
  // ── Step 8: Write install manifest ──
378
+ const pkgVersion = require('../../../package.json').version;
375
379
  const manifest = {
376
- version: '0.4.0',
380
+ version: pkgVersion,
377
381
  uiLanguage: lang,
378
382
  installed: new Date().toISOString(),
379
383
  packs: selectedPacks,
@@ -430,6 +434,40 @@ module.exports = {
430
434
  i.guide_or_auto,
431
435
  '',
432
436
  `${i.guide_output}: _bmad-output/discovery/ & _bmad-output/build/`,
437
+ '',
438
+ '─'.repeat(50),
439
+ '',
440
+ `📦 ${i.guide_cli_title || 'CLI Commands'}:`,
441
+ ` npx bmad-plus install ${i.guide_cli_install || '— Install agents & skills'}`,
442
+ ` npx bmad-plus update ${i.guide_cli_update || '— Update agents (keeps config)'}`,
443
+ ` npx bmad-plus doctor ${i.guide_cli_doctor || '— Check installation health'}`,
444
+ ` npx bmad-plus uninstall ${i.guide_cli_uninstall || '— Remove BMAD+ from project'}`,
445
+ );
446
+
447
+ // Add pack-specific examples
448
+ const examples = [];
449
+ if (selectedPacks.includes('seo')) {
450
+ examples.push(` ${i.guide_example_seo || '🔍 SEO: "/seo audit https://example.com"'}`);
451
+ }
452
+ if (selectedPacks.includes('backup')) {
453
+ examples.push(` ${i.guide_example_backup || '🗂️ Backup: "/backup create" → ZIP timestamped'}`);
454
+ }
455
+ if (selectedPacks.includes('animated')) {
456
+ examples.push(` ${i.guide_example_animated || '🎬 Animated: "/animated build hero.mp4"'}`);
457
+ }
458
+ if (selectedPacks.includes('osint')) {
459
+ examples.push(` ${i.guide_example_osint || '🔍 OSINT: "Shadow, investigate John Doe"'}`);
460
+ }
461
+
462
+ if (examples.length > 0) {
463
+ agentGuide.push(
464
+ '',
465
+ `💡 ${i.guide_examples_title || 'Quick Examples'}:`,
466
+ ...examples
467
+ );
468
+ }
469
+
470
+ agentGuide.push(
433
471
  '',
434
472
  '---',
435
473
  i.guide_credits
@@ -1,6 +1,9 @@
1
1
  /**
2
2
  * BMAD+ Uninstall Command
3
3
  * Removes BMAD+ agents, skills, and configs from the current project
4
+ * Internationalized — uses i18n system
5
+ *
6
+ * Author: Laurent Rochetta
4
7
  */
5
8
 
6
9
  const path = require('node:path');
@@ -8,6 +11,7 @@ const fs = require('node:fs');
8
11
  const fsExtra = require('fs-extra');
9
12
  const clack = require('@clack/prompts');
10
13
  const pc = require('picocolors');
14
+ const { t, getLanguageOptions } = require('../i18n');
11
15
 
12
16
  module.exports = {
13
17
  command: 'uninstall',
@@ -26,45 +30,67 @@ module.exports = {
26
30
  }
27
31
 
28
32
  const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
29
- clack.log.info(`Found BMAD+ v${manifest.version} (installed ${manifest.installed})`);
33
+
34
+ // Use the language from install manifest, or ask
35
+ let lang = manifest.uiLanguage || 'en';
36
+ const i = t(lang);
37
+
38
+ clack.log.info(`BMAD+ v${manifest.version} (${i.installed_on || 'installed'} ${manifest.installed.split('T')[0]})`);
39
+ clack.log.info(`${i.selected_packs}: ${(manifest.packs || ['core']).join(', ')}`);
30
40
 
31
41
  const confirm = await clack.confirm({
32
- message: 'Supprimer BMAD+ de ce projet ?',
42
+ message: i.uninstall_confirm || 'Remove BMAD+ from this project?',
33
43
  });
34
44
 
35
45
  if (!confirm || clack.isCancel(confirm)) {
36
- clack.cancel('Cancelled.');
46
+ clack.cancel(i.cancelled);
37
47
  return;
38
48
  }
39
49
 
40
50
  const spinner = clack.spinner();
41
- spinner.start('Suppression...');
51
+ spinner.start(i.uninstall_removing || 'Removing...');
52
+
53
+ let removed = 0;
42
54
 
43
55
  // Remove .agents/skills/ (BMAD+ agents & skills)
44
56
  const agentsDir = path.join(projectDir, '.agents');
45
57
  if (fs.existsSync(agentsDir)) {
46
58
  fsExtra.removeSync(agentsDir);
59
+ removed++;
47
60
  }
48
61
 
49
62
  // Remove _bmad/ config
50
63
  const bmadDir = path.join(projectDir, '_bmad');
51
64
  if (fs.existsSync(bmadDir)) {
52
65
  fsExtra.removeSync(bmadDir);
66
+ removed++;
67
+ }
68
+
69
+ // Remove _bmad-output/ (only if empty)
70
+ const outputDir = path.join(projectDir, '_bmad-output');
71
+ if (fs.existsSync(outputDir)) {
72
+ const contents = fs.readdirSync(outputDir, { recursive: true }).filter(f => !f.startsWith('.'));
73
+ if (contents.length === 0) {
74
+ fsExtra.removeSync(outputDir);
75
+ removed++;
76
+ } else {
77
+ clack.log.info(i.uninstall_output_kept || '📁 _bmad-output/ kept (contains files)');
78
+ }
53
79
  }
54
80
 
55
- // Remove IDE configs
81
+ // Remove IDE configs (only BMAD+-generated ones)
56
82
  for (const configFile of ['CLAUDE.md', 'GEMINI.md', 'AGENTS.md', 'OPENCODE.md']) {
57
83
  const p = path.join(projectDir, configFile);
58
84
  if (fs.existsSync(p)) {
59
- // Only remove if it's a BMAD+ generated file
60
85
  const content = fs.readFileSync(p, 'utf8');
61
86
  if (content.includes('BMAD+')) {
62
87
  fs.unlinkSync(p);
88
+ removed++;
63
89
  }
64
90
  }
65
91
  }
66
92
 
67
- spinner.stop('✅ BMAD+ supprimé');
68
- clack.outro(pc.green('Done!'));
93
+ spinner.stop(i.uninstall_done ? i.uninstall_done(removed) : `✅ BMAD+ removed (${removed} items)`);
94
+ clack.outro(pc.green(i.guide_ready ? '👋 Done!' : 'Done!'));
69
95
  },
70
96
  };
@@ -0,0 +1,172 @@
1
+ /**
2
+ * BMAD+ Update Command
3
+ * Updates agents and skills while preserving user config
4
+ *
5
+ * Author: Laurent Rochetta
6
+ */
7
+
8
+ const path = require('node:path');
9
+ const fs = require('node:fs');
10
+ const fsExtra = require('fs-extra');
11
+ const clack = require('@clack/prompts');
12
+ const pc = require('picocolors');
13
+ const { t } = require('../i18n');
14
+
15
+ // Same pack definitions as install.js — keep in sync
16
+ const PACKS = {
17
+ core: {
18
+ agents: ['agent-strategist', 'agent-architect-dev', 'agent-quality', 'agent-orchestrator'],
19
+ skills: ['bmad-plus-autopilot', 'bmad-plus-parallel', 'bmad-plus-sync'],
20
+ data: ['role-triggers.yaml'],
21
+ },
22
+ osint: {
23
+ agents: ['agent-shadow'],
24
+ skills: [],
25
+ externalPackage: 'osint-agent-package',
26
+ },
27
+ maker: {
28
+ agents: ['agent-maker'],
29
+ skills: [],
30
+ data: [],
31
+ },
32
+ seo: { agents: [], skills: [], packDir: 'pack-seo' },
33
+ backup: { agents: [], skills: [], packDir: 'pack-backup' },
34
+ animated: { agents: [], skills: [], packDir: 'pack-animated' },
35
+ };
36
+
37
+ module.exports = {
38
+ command: 'update',
39
+ description: 'Update BMAD+ agents and skills (preserves config)',
40
+ options: [
41
+ ['-d, --directory <path>', 'Project directory (default: current directory)'],
42
+ ],
43
+ action: async (options) => {
44
+ const projectDir = path.resolve(options.directory || process.cwd());
45
+ const bmadSrc = path.join(__dirname, '..', '..', '..', 'src', 'bmad-plus');
46
+ const packageJson = require('../../../package.json');
47
+
48
+ clack.intro(pc.bgMagenta(pc.white(` BMAD+ Updater v${packageJson.version} `)));
49
+
50
+ // Check if installed
51
+ const manifestPath = path.join(projectDir, '_bmad', '.bmad-plus-install.json');
52
+ if (!fs.existsSync(manifestPath)) {
53
+ clack.log.error('BMAD+ is not installed in this directory.');
54
+ clack.log.info('Run `npx bmad-plus install` first.');
55
+ clack.outro(pc.red('Update aborted.'));
56
+ return;
57
+ }
58
+
59
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
60
+ const lang = manifest.uiLanguage || 'en';
61
+ const i = t(lang);
62
+
63
+ clack.log.info(`📦 Installed: v${manifest.version} → Available: v${packageJson.version}`);
64
+
65
+ if (manifest.version === packageJson.version) {
66
+ clack.log.success(i.update_current || '✅ Already up to date!');
67
+ clack.outro(pc.green('Nothing to update.'));
68
+ return;
69
+ }
70
+
71
+ const selectedPacks = manifest.packs || ['core'];
72
+ clack.log.info(`${i.selected_packs}: ${selectedPacks.join(', ')}`);
73
+
74
+ const confirm = await clack.confirm({
75
+ message: i.update_confirm || `Update from v${manifest.version} to v${packageJson.version}?`,
76
+ });
77
+
78
+ if (!confirm || clack.isCancel(confirm)) {
79
+ clack.cancel(i.cancelled);
80
+ return;
81
+ }
82
+
83
+ const spinner = clack.spinner();
84
+ spinner.start(i.update_updating || 'Updating agents and skills...');
85
+
86
+ const targetAgentsDir = path.join(projectDir, '.agents', 'skills');
87
+ const targetDataDir = path.join(projectDir, '.agents', 'data');
88
+
89
+ fsExtra.ensureDirSync(targetAgentsDir);
90
+ fsExtra.ensureDirSync(targetDataDir);
91
+
92
+ let updated = 0;
93
+
94
+ for (const packId of selectedPacks) {
95
+ const pack = PACKS[packId];
96
+ if (!pack) continue;
97
+
98
+ // Update agents
99
+ for (const agent of (pack.agents || [])) {
100
+ const src = path.join(bmadSrc, 'agents', agent);
101
+ const dest = path.join(targetAgentsDir, agent);
102
+ if (fs.existsSync(src)) {
103
+ fsExtra.copySync(src, dest, { overwrite: true });
104
+ updated++;
105
+ }
106
+ }
107
+
108
+ // Update skills
109
+ for (const skill of (pack.skills || [])) {
110
+ const src = path.join(bmadSrc, 'skills', skill);
111
+ const dest = path.join(targetAgentsDir, skill);
112
+ if (fs.existsSync(src)) {
113
+ fsExtra.copySync(src, dest, { overwrite: true });
114
+ updated++;
115
+ }
116
+ }
117
+
118
+ // Update data files
119
+ for (const dataFile of (pack.data || [])) {
120
+ const src = path.join(bmadSrc, 'data', dataFile);
121
+ const dest = path.join(targetDataDir, dataFile);
122
+ if (fs.existsSync(src)) {
123
+ fsExtra.copySync(src, dest, { overwrite: true });
124
+ updated++;
125
+ }
126
+ }
127
+
128
+ // Update external package (OSINT)
129
+ if (pack.externalPackage) {
130
+ const extSrc = path.join(__dirname, '..', '..', '..', pack.externalPackage, 'skills');
131
+ if (fs.existsSync(extSrc)) {
132
+ fsExtra.copySync(extSrc, targetAgentsDir, { overwrite: true });
133
+ updated++;
134
+ }
135
+ }
136
+
137
+ // Update pack directory (SEO, Backup, Animated)
138
+ if (pack.packDir) {
139
+ const packSrc = path.join(bmadSrc, 'agents', pack.packDir);
140
+ const packDest = path.join(targetAgentsDir, pack.packDir);
141
+ if (fs.existsSync(packSrc)) {
142
+ fsExtra.copySync(packSrc, packDest, { overwrite: true });
143
+ updated++;
144
+ }
145
+ }
146
+ }
147
+
148
+ // Update module config (always)
149
+ const moduleYaml = path.join(bmadSrc, 'module.yaml');
150
+ const targetBmadDir = path.join(projectDir, '_bmad');
151
+ if (fs.existsSync(moduleYaml)) {
152
+ fsExtra.copySync(moduleYaml, path.join(targetBmadDir, 'module.yaml'));
153
+ updated++;
154
+ }
155
+
156
+ const helpCsv = path.join(bmadSrc, 'module-help.csv');
157
+ if (fs.existsSync(helpCsv)) {
158
+ fsExtra.copySync(helpCsv, path.join(targetBmadDir, 'module-help.csv'));
159
+ updated++;
160
+ }
161
+
162
+ // Update manifest version (preserve everything else)
163
+ manifest.version = packageJson.version;
164
+ manifest.lastUpdated = new Date().toISOString();
165
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), 'utf8');
166
+
167
+ spinner.stop(i.update_done ? i.update_done(updated) : `✅ ${updated} files updated to v${packageJson.version}`);
168
+
169
+ clack.log.info('📋 Config preserved: config.yaml, IDE configs, output directories');
170
+ clack.outro(pc.green(i.update_ready || `BMAD+ v${packageJson.version} is ready! 🚀`));
171
+ },
172
+ };