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,102 +1,102 @@
1
- /**
2
- * BMAD+ Stack Detection — Shared Module
3
- * Unified project stack detection for autoconfig and scan commands.
4
- * Merges patterns from both original implementations.
5
- */
6
-
7
- const path = require('node:path');
8
- const fs = require('node:fs');
9
-
10
- // ── Project Detection Markers (for scan.js) ──
11
-
12
- const PROJECT_MARKERS = [
13
- { file: 'package.json', stack: 'Node.js', detect: (dir) => {
14
- try {
15
- const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
16
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
17
- if (deps['next']) return 'Next.js';
18
- if (deps['nuxt']) return 'Nuxt';
19
- if (deps['react']) return 'React';
20
- if (deps['vue']) return 'Vue.js';
21
- if (deps['svelte']) return 'Svelte';
22
- if (deps['express']) return 'Express';
23
- if (deps['fastify']) return 'Fastify';
24
- if (deps['electron']) return 'Electron';
25
- if (deps['tauri']) return 'Tauri';
26
- return 'Node.js';
27
- } catch {
28
- return 'Node.js';
29
- }
30
- }},
31
- { file: 'Cargo.toml', stack: 'Rust' },
32
- { file: 'pyproject.toml', stack: 'Python' },
33
- { file: 'requirements.txt', stack: 'Python' },
34
- { file: 'go.mod', stack: 'Go' },
35
- { file: 'composer.json', stack: 'PHP' },
36
- { file: 'Gemfile', stack: 'Ruby' },
37
- { file: 'pom.xml', stack: 'Java' },
38
- { file: 'build.gradle', stack: 'Java/Kotlin' },
39
- ];
40
-
41
- // ── Full Stack Detection (for autoconfig.js) ──
42
-
43
- function detectStack(dir) {
44
- const result = {
45
- language: null,
46
- framework: null,
47
- runtime: null,
48
- packageManager: null,
49
- hasTypeScript: false,
50
- };
51
-
52
- // Package.json analysis
53
- const pkgPath = path.join(dir, 'package.json');
54
- if (fs.existsSync(pkgPath)) {
55
- try {
56
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
57
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
58
- result.runtime = 'Node.js';
59
- result.language = deps['typescript'] || fs.existsSync(path.join(dir, 'tsconfig.json')) ? 'TypeScript' : 'JavaScript';
60
- result.hasTypeScript = result.language === 'TypeScript';
61
-
62
- // Framework detection
63
- if (deps['next']) result.framework = 'Next.js';
64
- else if (deps['nuxt']) result.framework = 'Nuxt';
65
- else if (deps['@angular/core']) result.framework = 'Angular';
66
- else if (deps['react']) result.framework = 'React';
67
- else if (deps['vue']) result.framework = 'Vue.js';
68
- else if (deps['svelte']) result.framework = 'Svelte';
69
- else if (deps['express']) result.framework = 'Express';
70
- else if (deps['fastify']) result.framework = 'Fastify';
71
- else if (deps['hono']) result.framework = 'Hono';
72
- else if (deps['electron']) result.framework = 'Electron';
73
- else if (deps['tauri']) result.framework = 'Tauri';
74
- else if (deps['react-native']) result.framework = 'React Native';
75
-
76
- // Package manager
77
- if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) result.packageManager = 'pnpm';
78
- else if (fs.existsSync(path.join(dir, 'yarn.lock'))) result.packageManager = 'yarn';
79
- else if (fs.existsSync(path.join(dir, 'bun.lockb'))) result.packageManager = 'bun';
80
- else result.packageManager = 'npm';
81
- } catch (e) {
82
- console.warn('stack-detect: Failed to parse package.json in detectStack', e.message);
83
- }
84
- }
85
-
86
- // Other languages
87
- if (!result.runtime) {
88
- if (fs.existsSync(path.join(dir, 'Cargo.toml'))) { result.language = 'Rust'; result.runtime = 'Rust'; }
89
- else if (fs.existsSync(path.join(dir, 'pyproject.toml')) || fs.existsSync(path.join(dir, 'requirements.txt'))) { result.language = 'Python'; result.runtime = 'Python'; }
90
- else if (fs.existsSync(path.join(dir, 'go.mod'))) { result.language = 'Go'; result.runtime = 'Go'; }
91
- else if (fs.existsSync(path.join(dir, 'composer.json'))) { result.language = 'PHP'; result.runtime = 'PHP'; }
92
- else if (fs.existsSync(path.join(dir, 'Gemfile'))) { result.language = 'Ruby'; result.runtime = 'Ruby'; }
93
- else if (fs.existsSync(path.join(dir, 'pom.xml')) || fs.existsSync(path.join(dir, 'build.gradle'))) { result.language = 'Java'; result.runtime = 'JVM'; }
94
- }
95
-
96
- return result;
97
- }
98
-
99
- module.exports = {
100
- detectStack,
101
- PROJECT_MARKERS,
102
- };
1
+ /**
2
+ * BMAD+ Stack Detection — Shared Module
3
+ * Unified project stack detection for autoconfig and scan commands.
4
+ * Merges patterns from both original implementations.
5
+ */
6
+
7
+ const path = require('node:path');
8
+ const fs = require('node:fs');
9
+
10
+ // ── Project Detection Markers (for scan.js) ──
11
+
12
+ const PROJECT_MARKERS = [
13
+ { file: 'package.json', stack: 'Node.js', detect: (dir) => {
14
+ try {
15
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
16
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
17
+ if (deps['next']) return 'Next.js';
18
+ if (deps['nuxt']) return 'Nuxt';
19
+ if (deps['react']) return 'React';
20
+ if (deps['vue']) return 'Vue.js';
21
+ if (deps['svelte']) return 'Svelte';
22
+ if (deps['express']) return 'Express';
23
+ if (deps['fastify']) return 'Fastify';
24
+ if (deps['electron']) return 'Electron';
25
+ if (deps['tauri']) return 'Tauri';
26
+ return 'Node.js';
27
+ } catch {
28
+ return 'Node.js';
29
+ }
30
+ }},
31
+ { file: 'Cargo.toml', stack: 'Rust' },
32
+ { file: 'pyproject.toml', stack: 'Python' },
33
+ { file: 'requirements.txt', stack: 'Python' },
34
+ { file: 'go.mod', stack: 'Go' },
35
+ { file: 'composer.json', stack: 'PHP' },
36
+ { file: 'Gemfile', stack: 'Ruby' },
37
+ { file: 'pom.xml', stack: 'Java' },
38
+ { file: 'build.gradle', stack: 'Java/Kotlin' },
39
+ ];
40
+
41
+ // ── Full Stack Detection (for autoconfig.js) ──
42
+
43
+ function detectStack(dir) {
44
+ const result = {
45
+ language: null,
46
+ framework: null,
47
+ runtime: null,
48
+ packageManager: null,
49
+ hasTypeScript: false,
50
+ };
51
+
52
+ // Package.json analysis
53
+ const pkgPath = path.join(dir, 'package.json');
54
+ if (fs.existsSync(pkgPath)) {
55
+ try {
56
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
57
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
58
+ result.runtime = 'Node.js';
59
+ result.language = deps['typescript'] || fs.existsSync(path.join(dir, 'tsconfig.json')) ? 'TypeScript' : 'JavaScript';
60
+ result.hasTypeScript = result.language === 'TypeScript';
61
+
62
+ // Framework detection
63
+ if (deps['next']) result.framework = 'Next.js';
64
+ else if (deps['nuxt']) result.framework = 'Nuxt';
65
+ else if (deps['@angular/core']) result.framework = 'Angular';
66
+ else if (deps['react']) result.framework = 'React';
67
+ else if (deps['vue']) result.framework = 'Vue.js';
68
+ else if (deps['svelte']) result.framework = 'Svelte';
69
+ else if (deps['express']) result.framework = 'Express';
70
+ else if (deps['fastify']) result.framework = 'Fastify';
71
+ else if (deps['hono']) result.framework = 'Hono';
72
+ else if (deps['electron']) result.framework = 'Electron';
73
+ else if (deps['tauri']) result.framework = 'Tauri';
74
+ else if (deps['react-native']) result.framework = 'React Native';
75
+
76
+ // Package manager
77
+ if (fs.existsSync(path.join(dir, 'pnpm-lock.yaml'))) result.packageManager = 'pnpm';
78
+ else if (fs.existsSync(path.join(dir, 'yarn.lock'))) result.packageManager = 'yarn';
79
+ else if (fs.existsSync(path.join(dir, 'bun.lockb'))) result.packageManager = 'bun';
80
+ else result.packageManager = 'npm';
81
+ } catch (e) {
82
+ console.warn('stack-detect: Failed to parse package.json in detectStack', e.message);
83
+ }
84
+ }
85
+
86
+ // Other languages
87
+ if (!result.runtime) {
88
+ if (fs.existsSync(path.join(dir, 'Cargo.toml'))) { result.language = 'Rust'; result.runtime = 'Rust'; }
89
+ else if (fs.existsSync(path.join(dir, 'pyproject.toml')) || fs.existsSync(path.join(dir, 'requirements.txt'))) { result.language = 'Python'; result.runtime = 'Python'; }
90
+ else if (fs.existsSync(path.join(dir, 'go.mod'))) { result.language = 'Go'; result.runtime = 'Go'; }
91
+ else if (fs.existsSync(path.join(dir, 'composer.json'))) { result.language = 'PHP'; result.runtime = 'PHP'; }
92
+ else if (fs.existsSync(path.join(dir, 'Gemfile'))) { result.language = 'Ruby'; result.runtime = 'Ruby'; }
93
+ else if (fs.existsSync(path.join(dir, 'pom.xml')) || fs.existsSync(path.join(dir, 'build.gradle'))) { result.language = 'Java'; result.runtime = 'JVM'; }
94
+ }
95
+
96
+ return result;
97
+ }
98
+
99
+ module.exports = {
100
+ detectStack,
101
+ PROJECT_MARKERS,
102
+ };
@@ -0,0 +1,153 @@
1
+ /** Bounded release discovery. This module never downloads or applies a package. */
2
+ const fs = require('node:fs');
3
+ const semver = require('semver');
4
+ const { safeAdapterPath } = require('./installed-adapters');
5
+ const { readInstallManifest } = require('./install-manifest');
6
+ const { readUpdatePolicy, validatePolicy, authorizeTarget, isExactVersion, writeProjectJson } = require('./update-policy');
7
+
8
+ const CACHE_FILE = '.bmad/update-check.json';
9
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
10
+ const FAILURE_BACKOFF_MS = 60 * 60 * 1000;
11
+
12
+ function validateRelease(value) {
13
+ if (!value || typeof value !== 'object' || Array.isArray(value) || !isExactVersion(value.version)) {
14
+ throw new Error('Registry returned an invalid exact version.');
15
+ }
16
+ const engines = value.engines === undefined ? {} : value.engines;
17
+ if (!engines || typeof engines !== 'object' || Array.isArray(engines) ||
18
+ (engines.node !== undefined && (typeof engines.node !== 'string' ||
19
+ !engines.node.trim() || !semver.validRange(engines.node)))) {
20
+ throw new Error('Registry returned an invalid Node engine requirement.');
21
+ }
22
+ return { version: value.version, engines: engines.node ? { node: engines.node } : {} };
23
+ }
24
+
25
+ async function queryNpmRelease({ projectDir, channel, registry }) {
26
+ validatePolicy({ channel, registry });
27
+ const { runNpm } = require('./npm-runner');
28
+ const stdout = await runNpm([
29
+ 'view', `bmad-plus@${channel}`, 'version', 'engines', '--json', `--registry=${registry}`,
30
+ '--prefer-online', '--fetch-timeout=4000', '--fetch-retries=0',
31
+ ], { cwd: projectDir, timeout: 6000, maxBuffer: 64 * 1024 });
32
+ return validateRelease(JSON.parse(stdout));
33
+ }
34
+
35
+ function timestamp(value, now) {
36
+ const time = typeof value === 'string' ? Date.parse(value) : NaN;
37
+ return Number.isFinite(time) && time <= now ? time : null;
38
+ }
39
+
40
+ function readCache(projectDir, policy, now) {
41
+ try {
42
+ const cache = JSON.parse(fs.readFileSync(safeAdapterPath(projectDir, CACHE_FILE), 'utf8'));
43
+ if (!cache || cache.schemaVersion !== 1 || cache.package !== 'bmad-plus' ||
44
+ cache.registry !== policy.registry || cache.channel !== policy.channel) return null;
45
+ if (cache.release) {
46
+ cache.release = validateRelease(cache.release);
47
+ if (timestamp(cache.checkedAt, now) === null) return null;
48
+ }
49
+ if (cache.failedAt !== undefined && timestamp(cache.failedAt, now) === null) return null;
50
+ return cache;
51
+ } catch { return null; }
52
+ }
53
+
54
+ function saveCache(projectDir, cache) {
55
+ try { writeProjectJson(projectDir, CACHE_FILE, cache); } catch { /* A read-only cache is nonfatal. */ }
56
+ }
57
+
58
+ async function checkForUpdate({
59
+ projectDir, runningVersion = require('../../../package.json').version, refresh = false, offline = false,
60
+ now = Date.now, queryRelease = queryNpmRelease, checkReadiness, nodeVersion = process.version,
61
+ }) {
62
+ const time = typeof now === 'function' ? now() : now;
63
+ const result = {
64
+ schemaVersion: 1, installedVersion: null, runningVersion, latestVersion: null,
65
+ targetVersion: null, channel: null, status: 'unknown', checkedAt: null,
66
+ source: 'none', stale: false, updateAvailable: null, releaseEligible: false,
67
+ versionEligible: false, canAutoApply: false, engineCompatible: null, engines: {}, reason: null,
68
+ };
69
+ if (!isExactVersion(runningVersion) || !Number.isFinite(time) ||
70
+ !Number.isFinite(new Date(time).getTime()) || !semver.valid(nodeVersion)) {
71
+ return { ...result, status: 'invalid-runtime', reason: 'invalid-running-version-or-clock' };
72
+ }
73
+ try {
74
+ const manifest = readInstallManifest(safeAdapterPath(projectDir, '_bmad/.bmad-plus-install.json'));
75
+ if (!isExactVersion(manifest.version)) throw new Error('Invalid installed version');
76
+ result.installedVersion = manifest.version;
77
+ } catch {
78
+ return { ...result, status: 'invalid-installation', reason: 'missing-or-invalid-installation-manifest' };
79
+ }
80
+ let policy;
81
+ try { policy = readUpdatePolicy(projectDir); } catch {
82
+ return { ...result, status: 'invalid-policy', reason: 'unreadable-or-invalid-update-policy' };
83
+ }
84
+ result.channel = policy.channel;
85
+ if (policy.mode === 'off') return { ...result, status: 'off', reason: 'updates-disabled' };
86
+
87
+ let cache = readCache(projectDir, policy, time);
88
+ const cachedAt = cache?.release ? timestamp(cache.checkedAt, time) : null;
89
+ const failedAt = cache?.failedAt === undefined ? null : timestamp(cache.failedAt, time);
90
+ let release;
91
+ if (offline) {
92
+ result.reason = 'offline';
93
+ } else if (!refresh && failedAt !== null && time - failedAt < FAILURE_BACKOFF_MS) {
94
+ result.reason = 'registry-retry-backoff';
95
+ } else if (!refresh && cachedAt !== null && failedAt === null && time - cachedAt < CACHE_TTL_MS) {
96
+ release = cache.release;
97
+ result.source = 'cache';
98
+ result.checkedAt = cache.checkedAt;
99
+ } else {
100
+ try {
101
+ release = validateRelease(await queryRelease({ projectDir, channel: policy.channel, registry: policy.registry }));
102
+ cache = { schemaVersion: 1, package: 'bmad-plus', registry: policy.registry, channel: policy.channel,
103
+ release, checkedAt: new Date(time).toISOString() };
104
+ saveCache(projectDir, cache);
105
+ result.source = 'registry';
106
+ result.checkedAt = cache.checkedAt;
107
+ } catch {
108
+ result.reason = 'registry-unavailable';
109
+ saveCache(projectDir, { ...cache, schemaVersion: 1, package: 'bmad-plus',
110
+ registry: policy.registry, channel: policy.channel, failedAt: new Date(time).toISOString() });
111
+ }
112
+ }
113
+ if (!release) {
114
+ if (cache?.release) {
115
+ result.targetVersion = cache.release.version;
116
+ result.latestVersion = policy.channel === 'latest' ? cache.release.version : null;
117
+ result.engines = cache.release.engines;
118
+ result.checkedAt = cache.checkedAt;
119
+ result.source = 'stale-cache';
120
+ result.stale = true;
121
+ }
122
+ return result;
123
+ }
124
+
125
+ result.targetVersion = release.version;
126
+ result.latestVersion = policy.channel === 'latest' ? release.version : null;
127
+ result.engines = release.engines;
128
+ result.engineCompatible = !release.engines.node || semver.satisfies(nodeVersion, release.engines.node);
129
+ const comparison = semver.compare(release.version, result.installedVersion);
130
+ result.updateAvailable = comparison > 0;
131
+ result.status = comparison > 0 ? 'update-available' : comparison === 0 ? 'current' : 'ahead';
132
+ result.reason = comparison > 0 ? null : comparison === 0 ? 'installed-matches-channel' : 'installed-ahead-of-channel';
133
+ if (comparison <= 0) return result;
134
+ if (!result.engineCompatible) return { ...result, reason: 'incompatible-node-version' };
135
+ if (!policy.allowPrerelease && semver.prerelease(release.version)) {
136
+ return { ...result, reason: 'prerelease-not-allowed' };
137
+ }
138
+ result.releaseEligible = true;
139
+ const authorization = authorizeTarget(policy, release.version);
140
+ result.versionEligible = authorization.allowed;
141
+ result.reason = authorization.reason;
142
+ if (authorization.allowed) {
143
+ if (!checkReadiness) return { ...result, reason: 'ownership-readiness-not-checked' };
144
+ try {
145
+ const readiness = await checkReadiness({ projectDir, targetVersion: release.version, policy });
146
+ result.canAutoApply = readiness?.ready === true;
147
+ result.reason = result.canAutoApply ? 'ready-for-authorized-update' : readiness?.reason || 'ownership-not-ready';
148
+ } catch { result.reason = 'ownership-readiness-failed'; }
149
+ }
150
+ return result;
151
+ }
152
+
153
+ module.exports = { CACHE_FILE, CACHE_TTL_MS, FAILURE_BACKOFF_MS, checkForUpdate, queryNpmRelease };
@@ -0,0 +1,182 @@
1
+ /** Discover and execute one exact, approved BMAD+ release. */
2
+ const path = require('node:path');
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const assert = require('node:assert/strict');
6
+ const semver = require('semver');
7
+ const {
8
+ OFFICIAL_REGISTRY,
9
+ isExactVersion,
10
+ readUpdatePolicy,
11
+ authorizeTarget,
12
+ } = require('./update-policy');
13
+
14
+ function readProjectManifest(projectDir) {
15
+ const { safeAdapterPath } = require('./installed-adapters');
16
+ const { readInstallManifest } = require('./install-manifest');
17
+ return readInstallManifest(safeAdapterPath(projectDir, '_bmad/.bmad-plus-install.json'));
18
+ }
19
+
20
+ function refusal(reason, detail) {
21
+ return Object.assign(new Error(`Update refused: ${detail || reason}.`), {
22
+ code: 'UPDATE_REFUSED',
23
+ reason,
24
+ });
25
+ }
26
+
27
+ function requirePolicy(policy, targetVersion, auto) {
28
+ if (policy.registry !== OFFICIAL_REGISTRY) throw refusal('unsupported-registry');
29
+ if (policy.mode === 'off') throw refusal('updates-disabled');
30
+ if (semver.prerelease(targetVersion) && !policy.allowPrerelease)
31
+ throw refusal('prerelease-not-allowed');
32
+ if (auto) {
33
+ const authorization = authorizeTarget(policy, targetVersion);
34
+ if (!authorization.allowed) throw refusal(authorization.reason);
35
+ }
36
+ }
37
+
38
+ async function withExecutionDirectory(action) {
39
+ const parent = fs.realpathSync(os.tmpdir());
40
+ const directory = fs.mkdtempSync(path.join(parent, 'bmad-update-exec-'));
41
+ try {
42
+ // npm exec may otherwise select a project-local .cmd shim. npm's Windows
43
+ // shim fails on metacharacters in its own path; target argv is escaped by npm.
44
+ return await action(directory);
45
+ } finally {
46
+ const actual = fs.realpathSync(directory);
47
+ assert(
48
+ path.dirname(actual) === parent &&
49
+ path.basename(actual).startsWith('bmad-update-exec-') &&
50
+ !fs.lstatSync(directory).isSymbolicLink(),
51
+ 'Refusing to remove an unexpected updater temporary directory.'
52
+ );
53
+ fs.rmSync(actual, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
54
+ }
55
+ }
56
+
57
+ async function runLatestUpdate({ projectDir, auto = false, yes = false }, injectedDeps = {}) {
58
+ if (typeof auto !== 'boolean' || typeof yes !== 'boolean')
59
+ throw refusal('invalid-confirmation-options');
60
+ const directory = path.resolve(projectDir || process.cwd());
61
+ const checkForUpdate = injectedDeps.checkForUpdate || require('./update-check').checkForUpdate;
62
+ const runNpm = injectedDeps.runNpm || require('./npm-runner').runNpm;
63
+ const readManifest = injectedDeps.readManifest || readProjectManifest;
64
+ const readPolicy = injectedDeps.readPolicy || readUpdatePolicy;
65
+ const platform = injectedDeps.platform || process.platform;
66
+ const nodeVersion = injectedDeps.nodeVersion || process.version;
67
+ const executeInDirectory = injectedDeps.withExecutionDirectory || withExecutionDirectory;
68
+ const manifest = readManifest(directory);
69
+ if (!isExactVersion(manifest.version)) throw refusal('invalid-installation');
70
+ const initialPolicy = readPolicy(directory);
71
+ if (initialPolicy.mode === 'off') throw refusal('updates-disabled');
72
+ if (auto && initialPolicy.mode !== 'auto') throw refusal('automatic-updates-not-authorized');
73
+
74
+ const check = await checkForUpdate({ projectDir: directory, refresh: true });
75
+ if (!check || check.source !== 'registry' || check.stale !== false)
76
+ throw refusal('fresh-metadata-required');
77
+ const targetVersion = check.targetVersion;
78
+ if (!isExactVersion(targetVersion)) throw refusal('invalid-target-version');
79
+ if (check.channel !== initialPolicy.channel) throw refusal('policy-channel-changed');
80
+ if (check.installedVersion !== manifest.version)
81
+ throw refusal('installation-changed-during-discovery');
82
+ if (check.status === 'ahead' || semver.lt(targetVersion, manifest.version))
83
+ throw refusal('downgrade-not-allowed');
84
+ if (check.status === 'current' && semver.eq(targetVersion, manifest.version)) {
85
+ return { status: 'current', targetVersion, installedVersion: manifest.version, updated: false };
86
+ }
87
+ if (
88
+ check.status !== 'update-available' ||
89
+ check.updateAvailable !== true ||
90
+ check.releaseEligible !== true ||
91
+ check.engineCompatible !== true ||
92
+ !semver.gt(targetVersion, manifest.version)
93
+ ) {
94
+ throw refusal(check.reason || 'release-not-eligible');
95
+ }
96
+ const engine = check.engines?.node;
97
+ if (
98
+ !check.engines ||
99
+ typeof check.engines !== 'object' ||
100
+ Array.isArray(check.engines) ||
101
+ (engine !== undefined &&
102
+ (typeof engine !== 'string' ||
103
+ !semver.validRange(engine) ||
104
+ !semver.satisfies(nodeVersion, engine)))
105
+ ) {
106
+ throw refusal('incompatible-or-invalid-node-engine');
107
+ }
108
+ requirePolicy(initialPolicy, targetVersion, auto);
109
+
110
+ const args = [
111
+ 'exec',
112
+ '--yes',
113
+ `--package=bmad-plus@${targetVersion}`,
114
+ `--registry=${OFFICIAL_REGISTRY}`,
115
+ `--script-shell=${platform === 'win32' ? 'cmd.exe' : 'sh'}`,
116
+ '--',
117
+ 'bmad-plus',
118
+ 'update',
119
+ '--yes',
120
+ ...(auto ? ['--auto'] : []),
121
+ '--directory',
122
+ directory,
123
+ '--expected-version',
124
+ targetVersion,
125
+ ];
126
+ if (!auto && !yes) {
127
+ return {
128
+ status: 'approval-required',
129
+ reason: 'Confirm this exact update with --yes.',
130
+ installedVersion: manifest.version,
131
+ targetVersion,
132
+ updated: false,
133
+ command: ['npm', ...args],
134
+ };
135
+ }
136
+ if (auto) {
137
+ const evaluateReadiness =
138
+ injectedDeps.evaluateReadiness || require('./update-transaction').evaluateUpdateReadiness;
139
+ const readiness = await evaluateReadiness({ projectDir: directory, manifest });
140
+ if (readiness?.ready !== true || readiness.legacy || readiness.conflicts?.length) {
141
+ throw refusal(
142
+ 'ownership-preflight-failed',
143
+ readiness?.reason || 'The installation is not ready for an automatic update'
144
+ );
145
+ }
146
+ }
147
+ // Re-read authorization after asynchronous work, including potential revocation.
148
+ const finalPolicy = readPolicy(directory);
149
+ requirePolicy(finalPolicy, targetVersion, auto);
150
+ if (finalPolicy.channel !== check.channel) throw refusal('policy-channel-changed');
151
+ const beforeExec = readManifest(directory);
152
+ if (beforeExec.version !== manifest.version)
153
+ throw refusal('installation-changed-before-execution');
154
+ const output = await executeInDirectory((cwd) =>
155
+ runNpm(args, { cwd, timeout: 120000, maxBuffer: 4 * 1024 * 1024 })
156
+ );
157
+ const updated = readManifest(directory);
158
+ if (updated.version !== targetVersion) {
159
+ throw Object.assign(
160
+ new Error(
161
+ `Updater finished without the expected manifest version ${targetVersion}. Inspect the project before retrying.\n${output}`
162
+ ),
163
+ {
164
+ code: 'UPDATE_UNVERIFIED',
165
+ reason: 'manifest-version-mismatch',
166
+ targetVersion,
167
+ stdout: output,
168
+ }
169
+ );
170
+ }
171
+ return {
172
+ status: 'updated',
173
+ installedVersion: updated.version,
174
+ previousVersion: manifest.version,
175
+ targetVersion,
176
+ updated: true,
177
+ reloadInstructions: true,
178
+ output,
179
+ };
180
+ }
181
+
182
+ module.exports = { runLatestUpdate };
@@ -0,0 +1,90 @@
1
+ /** Explicit project policy for release discovery and automatic application. */
2
+ const fs = require('node:fs');
3
+ const path = require('node:path');
4
+ const crypto = require('node:crypto');
5
+ const semver = require('semver');
6
+ const { safeAdapterPath } = require('./installed-adapters');
7
+
8
+ const POLICY_FILE = '_bmad/update-policy.json';
9
+ const OFFICIAL_REGISTRY = 'https://registry.npmjs.org/';
10
+ const DEFAULT_POLICY = Object.freeze({
11
+ mode: 'notify', channel: 'latest', allowedRange: null,
12
+ allowPrerelease: false, registry: OFFICIAL_REGISTRY,
13
+ });
14
+
15
+ function isExactVersion(version) {
16
+ return typeof version === 'string' &&
17
+ /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(version) &&
18
+ semver.valid(version) !== null;
19
+ }
20
+
21
+ function validatePolicy(value) {
22
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
23
+ throw new Error('Update policy must be an object.');
24
+ }
25
+ if (value.schemaVersion !== undefined && value.schemaVersion !== 1) {
26
+ throw new Error('Unsupported update policy schemaVersion.');
27
+ }
28
+ const policy = { ...DEFAULT_POLICY, ...value };
29
+ delete policy.schemaVersion;
30
+ if (Object.keys(policy).some(key => !Object.hasOwn(DEFAULT_POLICY, key)) ||
31
+ !['off', 'notify', 'auto'].includes(policy.mode) ||
32
+ typeof policy.channel !== 'string' || !/^[a-z][a-z0-9_-]{0,31}$/.test(policy.channel) ||
33
+ typeof policy.allowPrerelease !== 'boolean' || policy.registry !== OFFICIAL_REGISTRY ||
34
+ (policy.allowedRange !== null && (typeof policy.allowedRange !== 'string' ||
35
+ !policy.allowedRange.trim() || !semver.validRange(policy.allowedRange)))) {
36
+ throw new Error('Invalid update policy: check mode, channel, range, prerelease flag and official registry.');
37
+ }
38
+ if (policy.mode === 'auto' && policy.allowedRange === null) {
39
+ throw new Error('Automatic updates require an explicit allowedRange.');
40
+ }
41
+ return policy;
42
+ }
43
+
44
+ function readUpdatePolicy(projectDir) {
45
+ const file = safeAdapterPath(projectDir, POLICY_FILE);
46
+ try {
47
+ return validatePolicy(JSON.parse(fs.readFileSync(file, 'utf8')));
48
+ } catch (error) {
49
+ if (error.code === 'ENOENT') return { ...DEFAULT_POLICY };
50
+ throw new Error(`Update policy is unreadable or invalid: ${error.message}`, { cause: error });
51
+ }
52
+ }
53
+
54
+ /** Atomic project-local JSON write; never follow linked destinations or parents. */
55
+ function writeProjectJson(projectDir, relativeFile, data) {
56
+ const file = safeAdapterPath(projectDir, relativeFile);
57
+ fs.mkdirSync(path.dirname(file), { recursive: true });
58
+ const temporaryName = `${relativeFile}.${process.pid}.${crypto.randomUUID()}.tmp`;
59
+ const temporary = safeAdapterPath(projectDir, temporaryName);
60
+ try {
61
+ fs.writeFileSync(temporary, JSON.stringify(data, null, 2) + '\n', { encoding: 'utf8', flag: 'wx' });
62
+ safeAdapterPath(projectDir, relativeFile);
63
+ fs.renameSync(temporary, file);
64
+ } finally {
65
+ try { fs.unlinkSync(temporary); } catch { /* Preserve the original write error if cleanup fails. */ }
66
+ }
67
+ }
68
+
69
+ function writeUpdatePolicy(projectDir, value) {
70
+ const policy = validatePolicy(value);
71
+ writeProjectJson(projectDir, POLICY_FILE, { schemaVersion: 1, ...policy });
72
+ return policy;
73
+ }
74
+
75
+ function authorizeTarget(value, version) {
76
+ let policy;
77
+ try { policy = validatePolicy(value); } catch { return { allowed: false, reason: 'invalid-policy' }; }
78
+ if (!isExactVersion(version)) return { allowed: false, reason: 'invalid-target-version' };
79
+ if (policy.mode !== 'auto') return { allowed: false, reason: policy.mode === 'off' ? 'updates-disabled' : 'approval-required' };
80
+ if (!policy.allowPrerelease && semver.prerelease(version)) return { allowed: false, reason: 'prerelease-not-allowed' };
81
+ if (!semver.satisfies(version, policy.allowedRange, { includePrerelease: policy.allowPrerelease })) {
82
+ return { allowed: false, reason: 'outside-allowed-range' };
83
+ }
84
+ return { allowed: true, reason: 'authorized-policy' };
85
+ }
86
+
87
+ module.exports = {
88
+ DEFAULT_POLICY, POLICY_FILE, OFFICIAL_REGISTRY, isExactVersion,
89
+ validatePolicy, readUpdatePolicy, writeUpdatePolicy, authorizeTarget, writeProjectJson,
90
+ };