bmad-plus 0.12.1 → 0.13.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 (176) hide show
  1. package/CHANGELOG.md +637 -580
  2. package/README.md +123 -120
  3. package/osint-agent-package/agents/osint-investigator.md +12 -0
  4. package/osint-agent-package/skills/bmad-osint-investigate/osint/SKILL.md +491 -482
  5. package/osint-agent-package/skills/bmad-osint-investigate/osint/assets/dossier-template.md +126 -126
  6. package/osint-agent-package/skills/bmad-osint-investigate/osint/assets/lawful-basis-record.md +48 -48
  7. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/content-extraction.md +100 -100
  8. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/gdpr-osint.md +48 -48
  9. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/platforms.md +130 -130
  10. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/psychoprofile.md +69 -69
  11. package/osint-agent-package/skills/bmad-osint-investigate/osint/references/tools.md +281 -281
  12. package/osint-agent-package/skills/bmad-osint-investigate/osint/scripts/mcp-client.py +136 -136
  13. package/package.json +106 -91
  14. package/readme-international/README.de.md +625 -594
  15. package/readme-international/README.es.md +642 -611
  16. package/readme-international/README.fr.md +640 -609
  17. package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +2 -0
  18. package/src/bmad-plus/agents/agent-shadow/SKILL.md +18 -0
  19. package/src/bmad-plus/data/role-triggers.yaml +52 -0
  20. package/src/bmad-plus/module.yaml +333 -283
  21. package/src/bmad-plus/packs/pack-animated/animated-website-agent.md +325 -325
  22. package/src/bmad-plus/packs/pack-animated/templates/animated-website-workflow.md +55 -55
  23. package/src/bmad-plus/packs/pack-backup/backup-agent.md +71 -71
  24. package/src/bmad-plus/packs/pack-backup/templates/backup-workflow.md +51 -51
  25. package/src/bmad-plus/packs/pack-dev-studio/README.md +162 -162
  26. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +73 -73
  27. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +61 -61
  28. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +95 -95
  29. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +95 -95
  30. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +134 -134
  31. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +80 -80
  32. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +73 -73
  33. package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +95 -95
  34. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +73 -73
  35. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +73 -73
  36. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +92 -92
  37. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +80 -80
  38. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +90 -90
  39. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +153 -153
  40. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +173 -173
  41. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +224 -224
  42. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +329 -329
  43. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +318 -318
  44. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +359 -359
  45. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +379 -379
  46. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +361 -361
  47. package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +81 -81
  48. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +67 -67
  49. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +85 -85
  50. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +35 -35
  51. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +49 -49
  52. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +131 -131
  53. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +89 -89
  54. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +300 -300
  55. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +428 -428
  56. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +73 -73
  57. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +80 -80
  58. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +484 -484
  59. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +193 -193
  60. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +175 -175
  61. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +110 -110
  62. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +1511 -1511
  63. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +298 -298
  64. package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +296 -296
  65. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +29 -29
  66. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +74 -74
  67. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +29 -29
  68. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +73 -73
  69. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +89 -89
  70. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +73 -73
  71. package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +29 -29
  72. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +141 -141
  73. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +37 -37
  74. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +75 -75
  75. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +6 -6
  76. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +110 -110
  77. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +176 -176
  78. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +67 -67
  79. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +86 -86
  80. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +179 -179
  81. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +66 -66
  82. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +127 -127
  83. package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +105 -105
  84. package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +120 -120
  85. package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +12 -12
  86. package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +328 -328
  87. package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +32 -32
  88. package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +81 -81
  89. package/src/bmad-plus/packs/pack-seo/SKILL.md +171 -171
  90. package/src/bmad-plus/packs/pack-seo/checklist.md +140 -140
  91. package/src/bmad-plus/packs/pack-seo/pagespeed-playbook.md +320 -320
  92. package/src/bmad-plus/packs/pack-seo/ref/audit-schema.json +187 -187
  93. package/src/bmad-plus/packs/pack-seo/ref/cwv-thresholds.md +87 -87
  94. package/src/bmad-plus/packs/pack-seo/ref/eeat-criteria.md +123 -123
  95. package/src/bmad-plus/packs/pack-seo/ref/geo-signals.md +167 -167
  96. package/src/bmad-plus/packs/pack-seo/ref/hreflang-rules.md +153 -153
  97. package/src/bmad-plus/packs/pack-seo/ref/quality-gates.md +133 -133
  98. package/src/bmad-plus/packs/pack-seo/ref/schema-catalog.md +91 -91
  99. package/src/bmad-plus/packs/pack-seo/ref/schema-templates.json +356 -356
  100. package/src/bmad-plus/packs/pack-seo/requirements.txt +17 -0
  101. package/src/bmad-plus/packs/pack-seo/scripts/seo_apis.py +456 -0
  102. package/src/bmad-plus/packs/pack-seo/scripts/seo_crawl.py +359 -0
  103. package/src/bmad-plus/packs/pack-seo/scripts/seo_fetch.py +303 -0
  104. package/src/bmad-plus/packs/pack-seo/scripts/seo_parse.py +255 -0
  105. package/src/bmad-plus/packs/pack-seo/scripts/seo_report.py +409 -0
  106. package/src/bmad-plus/packs/pack-seo/scripts/seo_screenshot.py +202 -0
  107. package/src/bmad-plus/packs/pack-seo/seo-chief.md +294 -294
  108. package/src/bmad-plus/packs/pack-seo/seo-judge.md +241 -241
  109. package/src/bmad-plus/packs/pack-seo/seo-scout.md +171 -171
  110. package/src/bmad-plus/packs/pack-seo/templates/seo-audit-workflow.md +241 -241
  111. package/src/bmad-plus/packs/pack-shield/README.md +6 -6
  112. package/src/bmad-plus/packs/pack-shield/SKILL.md +2 -2
  113. package/src/bmad-plus/packs/pack-shield/categories/accessibility-esg/csrd-agent.md +11 -11
  114. package/src/bmad-plus/packs/pack-shield/categories/accessibility-esg/section508-agent.md +11 -11
  115. package/src/bmad-plus/packs/pack-shield/categories/accessibility-esg/wcag-agent.md +11 -11
  116. package/src/bmad-plus/packs/pack-shield/categories/ai-governance/eu-ai-act-agent.md +11 -11
  117. package/src/bmad-plus/packs/pack-shield/categories/ai-governance/iso42001-agent.md +11 -11
  118. package/src/bmad-plus/packs/pack-shield/categories/ai-governance/nist-ai-rmf-agent.md +11 -11
  119. package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/cis-controls-agent.md +11 -11
  120. package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/ism-agent.md +11 -11
  121. package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/iso27001-agent.md +11 -11
  122. package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/nis2-agent.md +11 -11
  123. package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/nist-800-53-agent.md +11 -11
  124. package/src/bmad-plus/packs/pack-shield/categories/cybersecurity/nist-csf-agent.md +11 -11
  125. package/src/bmad-plus/packs/pack-shield/categories/defense-export/cmmc-agent.md +11 -11
  126. package/src/bmad-plus/packs/pack-shield/categories/defense-export/ear-agent.md +11 -11
  127. package/src/bmad-plus/packs/pack-shield/categories/defense-export/itar-agent.md +11 -11
  128. package/src/bmad-plus/packs/pack-shield/categories/defense-export/tsa-agent.md +11 -11
  129. package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/dora-agent.md +11 -11
  130. package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/fedramp-agent.md +11 -11
  131. package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/hipaa-agent.md +11 -11
  132. package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/pci-dss-agent.md +11 -11
  133. package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/soc2-agent.md +11 -11
  134. package/src/bmad-plus/packs/pack-shield/categories/industry-compliance/swift-csp-agent.md +11 -11
  135. package/src/bmad-plus/packs/pack-shield/shield-orchestrator.md +1 -1
  136. package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +1 -1
  137. package/tools/bmad-plus-npx.js +4 -2
  138. package/tools/build/adapters.config.js +60 -51
  139. package/tools/build/check-counts.js +626 -0
  140. package/tools/build/check-install-contract.js +298 -0
  141. package/tools/build/generate-adapters.js +252 -56
  142. package/tools/build/generate.js +187 -10
  143. package/tools/build/generated-adapters/.codex/AGENTS.md +20 -7
  144. package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +20 -7
  145. package/tools/build/generated-adapters/.opencode/AGENTS.md +20 -7
  146. package/tools/build/generated-adapters/AGENTS.md +20 -7
  147. package/tools/build/generated-adapters/CLAUDE.md +20 -7
  148. package/tools/build/generated-adapters/CONVENTIONS.md +20 -7
  149. package/tools/build/generated-adapters/GEMINI.md +20 -7
  150. package/tools/build/module.template.yaml +82 -0
  151. package/tools/cli/bmad-plus-cli.js +16 -1
  152. package/tools/cli/commands/autoconfig.js +470 -470
  153. package/tools/cli/commands/doctor.js +205 -233
  154. package/tools/cli/commands/install.js +543 -501
  155. package/tools/cli/commands/memory-journal-cmd.js +311 -311
  156. package/tools/cli/commands/memory.js +195 -195
  157. package/tools/cli/commands/scan.js +348 -348
  158. package/tools/cli/commands/uninstall.js +209 -101
  159. package/tools/cli/commands/update-check.js +31 -0
  160. package/tools/cli/commands/update-policy.js +39 -0
  161. package/tools/cli/commands/update.js +123 -134
  162. package/tools/cli/i18n.js +905 -845
  163. package/tools/cli/lib/README-memory-journal.md +125 -125
  164. package/tools/cli/lib/ide-config.js +10 -259
  165. package/tools/cli/lib/install-manifest.js +17 -0
  166. package/tools/cli/lib/installed-adapters.js +89 -0
  167. package/tools/cli/lib/npm-runner.js +177 -0
  168. package/tools/cli/lib/pack-copy.js +62 -66
  169. package/tools/cli/lib/packs.js +437 -3
  170. package/tools/cli/lib/python-provision.js +508 -508
  171. package/tools/cli/lib/stack-detect.js +102 -102
  172. package/tools/cli/lib/update-check.js +153 -0
  173. package/tools/cli/lib/update-dispatch.js +182 -0
  174. package/tools/cli/lib/update-policy.js +90 -0
  175. package/tools/cli/lib/update-transaction.js +334 -0
  176. package/tools/cli/lib/validate.js +50 -50
@@ -1,101 +1,209 @@
1
- /**
2
- * BMAD+ Uninstall Command
3
- * Removes BMAD+ agents, skills, and configs from the current project
4
- * Internationalized — uses i18n system
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 } = require('../i18n');
15
-
16
- module.exports = {
17
- command: 'uninstall',
18
- description: 'Remove BMAD+ from your project',
19
- options: [
20
- ['-l, --lang <code>', 'Language code: en, fr, es, de, pt-br, ru, zh, he, ja, it'],
21
- ],
22
- action: async (options) => {
23
- const projectDir = process.cwd();
24
-
25
- clack.intro(pc.bgRed(pc.white(' BMAD+ Uninstaller ')));
26
-
27
- // Check if installed
28
- const manifestPath = path.join(projectDir, '_bmad', '.bmad-plus-install.json');
29
- if (!fs.existsSync(manifestPath)) {
30
- clack.log.warn('BMAD+ is not installed in this directory.');
31
- clack.outro('Nothing to remove.');
32
- return;
33
- }
34
-
35
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
36
-
37
- // Use --lang flag, or fall back to manifest language
38
- let lang = options.lang || manifest.uiLanguage || 'en';
39
- const i = t(lang);
40
-
41
- clack.log.info(`BMAD+ v${manifest.version} (${i.installed_on || 'installed'} ${manifest.installed.split('T')[0]})`);
42
- clack.log.info(`${i.selected_packs}: ${(manifest.packs || ['core']).join(', ')}`);
43
-
44
- const confirm = await clack.confirm({
45
- message: i.uninstall_confirm || 'Remove BMAD+ from this project?',
46
- });
47
-
48
- if (!confirm || clack.isCancel(confirm)) {
49
- clack.cancel(i.cancelled);
50
- return;
51
- }
52
-
53
- const spinner = clack.spinner();
54
- spinner.start(i.uninstall_removing || 'Removing...');
55
-
56
- let removed = 0;
57
-
58
- // Remove .agents/skills/ (BMAD+ agents & skills)
59
- const agentsDir = path.join(projectDir, '.agents');
60
- if (fs.existsSync(agentsDir)) {
61
- fsExtra.removeSync(agentsDir);
62
- removed++;
63
- }
64
-
65
- // Remove _bmad/ config
66
- const bmadDir = path.join(projectDir, '_bmad');
67
- if (fs.existsSync(bmadDir)) {
68
- fsExtra.removeSync(bmadDir);
69
- removed++;
70
- }
71
-
72
- // Remove _bmad-output/ (only if empty)
73
- const outputDir = path.join(projectDir, '_bmad-output');
74
- if (fs.existsSync(outputDir)) {
75
- const contents = fs.readdirSync(outputDir, { recursive: true }).filter(f => !f.startsWith('.'));
76
- if (contents.length === 0) {
77
- fsExtra.removeSync(outputDir);
78
- removed++;
79
- } else {
80
- clack.log.info(i.uninstall_output_kept || '📁 _bmad-output/ kept (contains files)');
81
- }
82
- }
83
-
84
- // Remove IDE configs (only BMAD+-generated ones)
85
- for (const configFile of ['CLAUDE.md', 'GEMINI.md', 'AGENTS.md', 'OPENCODE.md']) {
86
- const p = path.join(projectDir, configFile);
87
- if (fs.existsSync(p)) {
88
- const content = fs.readFileSync(p, 'utf8');
89
- // NODE-05: only remove a config BMAD+ actually generated (it carries the
90
- // marker below). A hand-authored file that merely mentions "BMAD+" is kept.
91
- if (content.includes('BMAD+ — AI Agent Configuration')) {
92
- fs.unlinkSync(p);
93
- removed++;
94
- }
95
- }
96
- }
97
-
98
- spinner.stop(i.uninstall_done ? i.uninstall_done(removed) : `✅ BMAD+ removed (${removed} items)`);
99
- clack.outro(pc.green(i.guide_ready ? '👋 Done!' : 'Done!'));
100
- },
101
- };
1
+ /**
2
+ * BMAD+ Uninstall Command
3
+ * Removes identifiable BMAD+ files while preserving project memory and edits.
4
+ * Author: Laurent Rochetta
5
+ */
6
+
7
+ const path = require('node:path');
8
+ const fs = require('node:fs');
9
+ const clack = require('@clack/prompts');
10
+ const pc = require('picocolors');
11
+ const { t } = require('../i18n');
12
+ const { PACKS, DERIVED } = require('../lib/packs');
13
+ const { readInstallManifest } = require('../lib/install-manifest');
14
+ const { generateUserFiles, USER_CONFIG_MARKER } = require('../../build/generate-adapters');
15
+ const { contentHash } = require('../lib/installed-adapters');
16
+
17
+ // Never follow a junction/symlink in a path used for deletion, including parents.
18
+ function safeStat(projectDir, target) {
19
+ const relative = path.relative(projectDir, target);
20
+ if (!relative || relative === '..' || relative.startsWith('..' + path.sep) || path.isAbsolute(relative)) {
21
+ throw new Error('Path outside the project: ' + target);
22
+ }
23
+ let current = projectDir;
24
+ let stat;
25
+ for (const part of relative.split(path.sep)) {
26
+ current = path.join(current, part);
27
+ try {
28
+ stat = fs.lstatSync(current);
29
+ } catch (err) {
30
+ if (err.code === 'ENOENT') return undefined;
31
+ throw err;
32
+ }
33
+ if (stat.isSymbolicLink()) {
34
+ const error = new Error('Symbolic link or junction: ' + current);
35
+ error.code = 'BMAD_UNSAFE_LINK';
36
+ throw error;
37
+ }
38
+ }
39
+ return stat;
40
+ }
41
+
42
+ function removalPlan(projectDir, manifest, manifestPath) {
43
+ const packageRoot = path.join(__dirname, '..', '..', '..');
44
+ const sourceRoot = path.join(packageRoot, 'src', 'bmad-plus');
45
+ const files = new Map();
46
+ const directories = new Set();
47
+ const targetPath = (...parts) => path.join(projectDir, ...parts);
48
+
49
+ // Old manifests contain pack IDs, not file hashes. Match shipped files byte for
50
+ // byte; anything unknown or modified stays, including files inside a pack.
51
+ function matchSource(source, target) {
52
+ const targetStat = safeStat(projectDir, target);
53
+ if (!targetStat || !fs.existsSync(source)) return;
54
+ const sourceStat = fs.lstatSync(source);
55
+ if (sourceStat.isDirectory() && targetStat.isDirectory()) {
56
+ directories.add(target);
57
+ for (const name of fs.readdirSync(source)) matchSource(path.join(source, name), path.join(target, name));
58
+ } else if (sourceStat.isFile() && targetStat.isFile()) {
59
+ const expected = fs.readFileSync(source);
60
+ if (fs.readFileSync(target).equals(expected)) files.set(target, expected);
61
+ }
62
+ }
63
+
64
+ for (const relative of ['.agents', '.agents/skills', '.agents/data', '_bmad', '_bmad-output']) {
65
+ const target = targetPath(relative);
66
+ if (safeStat(projectDir, target)?.isDirectory()) directories.add(target);
67
+ }
68
+
69
+ for (const packId of manifest.packs) {
70
+ if (!Object.hasOwn(PACKS, packId)) throw new Error('Unknown installed pack: ' + packId);
71
+ const pack = PACKS[packId];
72
+ for (const name of pack.agents || []) matchSource(path.join(sourceRoot, 'agents', name), targetPath('.agents', 'skills', name));
73
+ for (const name of pack.skills || []) matchSource(path.join(sourceRoot, 'skills', name), targetPath('.agents', 'skills', name));
74
+ for (const name of pack.data || []) matchSource(path.join(sourceRoot, 'data', name), targetPath('.agents', 'data', name));
75
+ if (pack.packDir) matchSource(path.join(sourceRoot, pack.packSrcDir || 'agents', pack.packDir), targetPath('.agents', 'skills', pack.packDir));
76
+ if (pack.externalPackage) matchSource(path.join(packageRoot, pack.externalPackage, 'skills'), targetPath('.agents', 'skills'));
77
+ }
78
+ for (const name of ['module.yaml', 'module-help.csv']) matchSource(path.join(sourceRoot, name), targetPath('_bmad', name));
79
+ const renderOptions = {
80
+ packs: manifest.packs,
81
+ userName: typeof manifest.user === 'string' ? manifest.user : 'Developer',
82
+ language: typeof manifest.language === 'string' ? manifest.language : 'English',
83
+ };
84
+ const expectedFiles = new Map(generateUserFiles(DERIVED, renderOptions).map(entry => [entry.file, entry.content]));
85
+ const hashes = manifest.adapterHashes && typeof manifest.adapterHashes === 'object' && !Array.isArray(manifest.adapterHashes)
86
+ ? manifest.adapterHashes : {};
87
+ // Only registry-declared paths can become deletion candidates, even when the
88
+ // manifest contains arbitrary hash keys. Keep previously owned tool targets.
89
+ const claimed = new Set(Object.keys(hashes).filter(file => expectedFiles.has(file)));
90
+ if (Array.isArray(manifest.ides)) {
91
+ const tools = manifest.ides.filter(id => DERIVED.targets.adapters.some(adapter => adapter.tool === id));
92
+ if (tools.length) {
93
+ for (const entry of generateUserFiles(DERIVED, { ...renderOptions, tools })) {
94
+ claimed.add(entry.file);
95
+ expectedFiles.set(entry.file, entry.content);
96
+ }
97
+ }
98
+ }
99
+ const legacyFallback = !Array.isArray(manifest.ides) && Object.keys(hashes).length === 0;
100
+ for (const [file, rendered] of expectedFiles) {
101
+ if (!legacyFallback && !claimed.has(file)) continue;
102
+ const target = targetPath(file);
103
+ let stat;
104
+ try {
105
+ stat = safeStat(projectDir, target);
106
+ } catch (error) {
107
+ // A link with no ownership evidence is user content, not an uninstall target.
108
+ if (legacyFallback && error.code === 'BMAD_UNSAFE_LINK') continue;
109
+ throw error;
110
+ }
111
+ if (stat?.isFile()) {
112
+ const content = fs.readFileSync(target);
113
+ const text = content.toString('utf8');
114
+ const expectedHash = hashes[file];
115
+ const matches = expectedHash ? contentHash(text) === expectedHash : text === rendered;
116
+ if (text.includes(USER_CONFIG_MARKER) && matches) {
117
+ files.set(target, content);
118
+ let parent = path.dirname(target);
119
+ while (parent !== projectDir) {
120
+ directories.add(parent);
121
+ parent = path.dirname(parent);
122
+ }
123
+ }
124
+ }
125
+ }
126
+ // Keep config.yaml: it contains the user's settings and has no ownership hash.
127
+ files.set(manifestPath, fs.readFileSync(manifestPath));
128
+ return { files, directories };
129
+ }
130
+
131
+ module.exports = {
132
+ command: 'uninstall',
133
+ description: 'Remove BMAD+ from your project',
134
+ options: [
135
+ ['-y, --yes', 'Confirm removal without prompting'],
136
+ ['-l, --lang <code>', 'Language code: en, fr, es, de, pt-br, ru, zh, he, ja, it'],
137
+ ],
138
+ action: async (options) => {
139
+ const projectDir = fs.realpathSync(process.cwd());
140
+ let i = t(options.lang || 'en');
141
+ clack.intro(pc.bgRed(pc.white(' BMAD+ Uninstaller ')));
142
+
143
+ const manifestPath = path.join(projectDir, '_bmad', '.bmad-plus-install.json');
144
+ let manifest;
145
+ try {
146
+ if (!safeStat(projectDir, manifestPath)) {
147
+ clack.log.warn(i.uninstall_not_installed);
148
+ return;
149
+ }
150
+ manifest = readInstallManifest(manifestPath);
151
+ } catch (err) {
152
+ clack.log.error(i.manifest_invalid(err.message));
153
+ clack.outro(pc.red(i.manifest_repair));
154
+ process.exitCode = 1;
155
+ return;
156
+ }
157
+ i = t(options.lang || manifest.uiLanguage || 'en');
158
+ if (!options.yes && !process.stdin.isTTY) {
159
+ clack.log.error(i.noninteractive_requires_yes('uninstall'));
160
+ process.exitCode = 1;
161
+ return;
162
+ }
163
+
164
+ let plan;
165
+ try {
166
+ plan = removalPlan(projectDir, manifest, manifestPath);
167
+ } catch (err) {
168
+ clack.log.error(i.uninstall_aborted(err.message));
169
+ process.exitCode = 1;
170
+ return;
171
+ }
172
+ clack.log.info('BMAD+ v' + manifest.version + ' (' + i.installed_on + ' ' + manifest.installed.split('T')[0] + ')');
173
+ clack.log.info(i.selected_packs + ': ' + manifest.packs.join(', '));
174
+ if (!options.yes) {
175
+ const confirm = await clack.confirm({ message: i.uninstall_confirm });
176
+ if (!confirm || clack.isCancel(confirm)) {
177
+ clack.cancel(i.cancelled);
178
+ return;
179
+ }
180
+ }
181
+
182
+ const spinner = clack.spinner();
183
+ spinner.start(i.uninstall_removing);
184
+ let removed = 0;
185
+ try {
186
+ // Revalidate after the prompt, before deleting any file.
187
+ for (const target of [...plan.files.keys(), ...plan.directories]) safeStat(projectDir, target);
188
+ for (const [target, expected] of plan.files) {
189
+ if (safeStat(projectDir, target)?.isFile() && fs.readFileSync(target).equals(expected)) {
190
+ fs.unlinkSync(target);
191
+ removed++;
192
+ }
193
+ }
194
+ // No recursive deletion: hidden files, nested user content, and memory stay.
195
+ for (const target of [...plan.directories].sort((a, b) => b.length - a.length)) {
196
+ if (safeStat(projectDir, target)?.isDirectory() && fs.readdirSync(target).length === 0) {
197
+ fs.rmdirSync(target);
198
+ removed++;
199
+ }
200
+ }
201
+ } catch (err) {
202
+ spinner.stop(i.uninstall_aborted(err.message));
203
+ process.exitCode = 1;
204
+ return;
205
+ }
206
+ spinner.stop(i.uninstall_done(removed));
207
+ clack.outro(i.uninstall_preserved);
208
+ },
209
+ };
@@ -0,0 +1,31 @@
1
+ /** Report release status without applying updates. */
2
+ const path = require('node:path');
3
+ const { checkForUpdate } = require('../lib/update-check');
4
+
5
+ module.exports = {
6
+ command: 'update-check',
7
+ description: 'Check the published BMAD+ release without applying it',
8
+ options: [
9
+ ['-d, --directory <path>', 'Project directory (default: current directory)'],
10
+ ['--refresh', 'Bypass the release cache and retry backoff'],
11
+ ['--offline', 'Report local state without network access'],
12
+ ['--json', 'Print machine-readable JSON'],
13
+ ],
14
+ action: async (options = {}) => {
15
+ const result = await checkForUpdate({
16
+ projectDir: path.resolve(options.directory || process.cwd()),
17
+ runningVersion: require('../../../package.json').version,
18
+ refresh: Boolean(options.refresh), offline: Boolean(options.offline),
19
+ checkReadiness: ({ projectDir }) => {
20
+ const { evaluateUpdateReadiness } = require('../lib/update-transaction');
21
+ return evaluateUpdateReadiness({ projectDir });
22
+ },
23
+ });
24
+ if (result.status.startsWith('invalid-')) process.exitCode = 1;
25
+ if (options.json) console.log(JSON.stringify(result));
26
+ else {
27
+ console.log(`Installed: ${result.installedVersion || 'unknown'}; running: ${result.runningVersion}; channel ${result.channel || 'unknown'}: ${result.targetVersion || 'unknown'}`);
28
+ console.log(`${result.status}: ${result.reason}${result.stale ? ' (cached result is stale)' : ''}`);
29
+ }
30
+ },
31
+ };
@@ -0,0 +1,39 @@
1
+ /** Show or explicitly configure the local update policy; never updates packages. */
2
+ const path = require('node:path');
3
+ const { readUpdatePolicy, writeUpdatePolicy } = require('../lib/update-policy');
4
+
5
+ module.exports = {
6
+ command: 'update-policy',
7
+ description: 'Show or configure the project update policy',
8
+ options: [
9
+ ['-d, --directory <path>', 'Project directory (default: current directory)'],
10
+ ['--mode <mode>', 'off, notify, or auto (auto requires --range)'],
11
+ ['--range <range>', 'Explicit semantic-version range for automatic updates'],
12
+ ['--channel <tag>', 'npm release tag (default: latest)'],
13
+ ['--allow-prerelease', 'Explicitly allow prerelease targets'],
14
+ ['--stable-only', 'Disable prerelease targets'],
15
+ ['--json', 'Print machine-readable JSON'],
16
+ ],
17
+ action: async (options = {}) => {
18
+ try {
19
+ if (options.allowPrerelease && options.stableOnly) throw new Error('Choose --allow-prerelease or --stable-only.');
20
+ if (options.mode === 'auto' && !options.range) throw new Error('--mode auto requires an explicit --range.');
21
+ const projectDir = path.resolve(options.directory || process.cwd());
22
+ const policy = readUpdatePolicy(projectDir);
23
+ const changing = options.mode !== undefined || options.range !== undefined ||
24
+ options.channel !== undefined || options.allowPrerelease || options.stableOnly;
25
+ if (options.mode !== undefined) policy.mode = options.mode;
26
+ if (options.range !== undefined) policy.allowedRange = options.range;
27
+ if (options.channel !== undefined) policy.channel = options.channel;
28
+ if (options.allowPrerelease) policy.allowPrerelease = true;
29
+ if (options.stableOnly) policy.allowPrerelease = false;
30
+ const saved = changing ? writeUpdatePolicy(projectDir, policy) : policy;
31
+ if (options.json) console.log(JSON.stringify({ policy: saved, changed: Boolean(changing) }));
32
+ else console.log(`Update policy${changing ? ' saved' : ''}: ${saved.mode}; channel=${saved.channel}; range=${saved.allowedRange || 'none'}; prereleases=${saved.allowPrerelease}`);
33
+ } catch (error) {
34
+ process.exitCode = 1;
35
+ if (options.json) console.log(JSON.stringify({ error: error.message }));
36
+ else console.error(error.message);
37
+ }
38
+ },
39
+ };