bmad-method 5.0.0-beta.2 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +3 -3
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +3 -3
  3. package/.github/workflows/discord.yaml +11 -2
  4. package/.github/workflows/format-check.yaml +42 -0
  5. package/.github/workflows/manual-release.yaml +173 -0
  6. package/.husky/pre-commit +3 -0
  7. package/.vscode/settings.json +26 -1
  8. package/CHANGELOG.md +0 -11
  9. package/README.md +2 -0
  10. package/bmad-core/agent-teams/team-all.yaml +1 -1
  11. package/bmad-core/agents/bmad-orchestrator.md +1 -1
  12. package/bmad-core/agents/dev.md +4 -4
  13. package/bmad-core/data/bmad-kb.md +1 -1
  14. package/bmad-core/data/test-levels-framework.md +12 -12
  15. package/bmad-core/tasks/facilitate-brainstorming-session.md +1 -1
  16. package/bmad-core/tasks/nfr-assess.md +10 -10
  17. package/bmad-core/tasks/qa-gate.md +23 -23
  18. package/bmad-core/tasks/review-story.md +18 -18
  19. package/bmad-core/tasks/risk-profile.md +25 -25
  20. package/bmad-core/tasks/test-design.md +9 -9
  21. package/bmad-core/tasks/trace-requirements.md +21 -21
  22. package/bmad-core/templates/architecture-tmpl.yaml +49 -49
  23. package/bmad-core/templates/brainstorming-output-tmpl.yaml +5 -5
  24. package/bmad-core/templates/brownfield-architecture-tmpl.yaml +31 -31
  25. package/bmad-core/templates/brownfield-prd-tmpl.yaml +13 -13
  26. package/bmad-core/templates/competitor-analysis-tmpl.yaml +19 -6
  27. package/bmad-core/templates/front-end-architecture-tmpl.yaml +21 -9
  28. package/bmad-core/templates/front-end-spec-tmpl.yaml +24 -24
  29. package/bmad-core/templates/fullstack-architecture-tmpl.yaml +122 -104
  30. package/bmad-core/templates/market-research-tmpl.yaml +2 -2
  31. package/bmad-core/templates/prd-tmpl.yaml +9 -9
  32. package/bmad-core/templates/project-brief-tmpl.yaml +4 -4
  33. package/bmad-core/templates/qa-gate-tmpl.yaml +9 -9
  34. package/bmad-core/templates/story-tmpl.yaml +12 -12
  35. package/bmad-core/workflows/brownfield-fullstack.yaml +9 -9
  36. package/bmad-core/workflows/brownfield-service.yaml +1 -1
  37. package/bmad-core/workflows/brownfield-ui.yaml +1 -1
  38. package/bmad-core/workflows/greenfield-fullstack.yaml +1 -1
  39. package/bmad-core/workflows/greenfield-service.yaml +1 -1
  40. package/bmad-core/workflows/greenfield-ui.yaml +1 -1
  41. package/common/utils/bmad-doc-template.md +5 -5
  42. package/dist/agents/analyst.txt +28 -15
  43. package/dist/agents/architect.txt +220 -190
  44. package/dist/agents/bmad-master.txt +298 -255
  45. package/dist/agents/bmad-orchestrator.txt +1 -1
  46. package/dist/agents/pm.txt +20 -20
  47. package/dist/agents/po.txt +11 -11
  48. package/dist/agents/qa.txt +275 -618
  49. package/dist/agents/sm.txt +11 -11
  50. package/dist/agents/ux-expert.txt +23 -23
  51. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +109 -109
  52. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +75 -77
  53. package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +41 -41
  54. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +483 -474
  55. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-architect.txt +1 -1
  56. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-designer.txt +149 -149
  57. package/dist/expansion-packs/bmad-2d-unity-game-dev/agents/game-sm.txt +20 -20
  58. package/dist/expansion-packs/bmad-2d-unity-game-dev/teams/unity-2d-game-team.txt +371 -358
  59. package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +25 -25
  60. package/dist/teams/team-all.txt +581 -881
  61. package/dist/teams/team-fullstack.txt +316 -273
  62. package/dist/teams/team-ide-minimal.txt +276 -619
  63. package/dist/teams/team-no-ui.txt +281 -238
  64. package/docs/versioning-and-releases.md +114 -44
  65. package/eslint.config.mjs +119 -0
  66. package/expansion-packs/Complete AI Agent System - Blank Templates & Google Cloud Setup/PART 1 - Google Cloud Vertex AI Setup Documentation/1.4 Deployment Configuration/1.4.2 - cloudbuild.yaml +26 -26
  67. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.md +4 -4
  68. package/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.md +1 -1
  69. package/expansion-packs/bmad-2d-phaser-game-dev/data/development-guidelines.md +26 -28
  70. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-architecture-tmpl.yaml +50 -50
  71. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-brief-tmpl.yaml +23 -23
  72. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-design-doc-tmpl.yaml +24 -24
  73. package/expansion-packs/bmad-2d-phaser-game-dev/templates/game-story-tmpl.yaml +42 -42
  74. package/expansion-packs/bmad-2d-phaser-game-dev/templates/level-design-doc-tmpl.yaml +65 -65
  75. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-dev-greenfield.yaml +5 -5
  76. package/expansion-packs/bmad-2d-phaser-game-dev/workflows/game-prototype.yaml +1 -1
  77. package/expansion-packs/bmad-2d-unity-game-dev/agents/game-developer.md +3 -3
  78. package/expansion-packs/bmad-2d-unity-game-dev/data/bmad-kb.md +1 -1
  79. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-brief-tmpl.yaml +23 -23
  80. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-design-doc-tmpl.yaml +63 -63
  81. package/expansion-packs/bmad-2d-unity-game-dev/templates/game-story-tmpl.yaml +20 -20
  82. package/expansion-packs/bmad-2d-unity-game-dev/templates/level-design-doc-tmpl.yaml +65 -65
  83. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-dev-greenfield.yaml +5 -5
  84. package/expansion-packs/bmad-2d-unity-game-dev/workflows/game-prototype.yaml +1 -1
  85. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-architecture-tmpl.yaml +20 -20
  86. package/expansion-packs/bmad-infrastructure-devops/templates/infrastructure-platform-from-arch-tmpl.yaml +7 -7
  87. package/package.json +62 -39
  88. package/prettier.config.mjs +32 -0
  89. package/release_notes.md +30 -0
  90. package/tools/bmad-npx-wrapper.js +10 -10
  91. package/tools/builders/web-builder.js +124 -130
  92. package/tools/bump-all-versions.js +42 -33
  93. package/tools/bump-expansion-version.js +23 -16
  94. package/tools/cli.js +10 -12
  95. package/tools/flattener/aggregate.js +10 -10
  96. package/tools/flattener/binary.js +44 -17
  97. package/tools/flattener/discovery.js +19 -18
  98. package/tools/flattener/files.js +6 -6
  99. package/tools/flattener/ignoreRules.js +125 -125
  100. package/tools/flattener/main.js +201 -304
  101. package/tools/flattener/projectRoot.js +75 -73
  102. package/tools/flattener/prompts.js +9 -9
  103. package/tools/flattener/stats.helpers.js +131 -67
  104. package/tools/flattener/stats.js +3 -3
  105. package/tools/flattener/test-matrix.js +201 -193
  106. package/tools/flattener/xml.js +33 -31
  107. package/tools/installer/bin/bmad.js +130 -89
  108. package/tools/installer/config/ide-agent-config.yaml +1 -1
  109. package/tools/installer/config/install.config.yaml +2 -2
  110. package/tools/installer/lib/config-loader.js +46 -42
  111. package/tools/installer/lib/file-manager.js +91 -113
  112. package/tools/installer/lib/ide-base-setup.js +57 -56
  113. package/tools/installer/lib/ide-setup.js +375 -343
  114. package/tools/installer/lib/installer.js +875 -714
  115. package/tools/installer/lib/memory-profiler.js +54 -53
  116. package/tools/installer/lib/module-manager.js +19 -15
  117. package/tools/installer/lib/resource-locator.js +26 -28
  118. package/tools/installer/package.json +19 -19
  119. package/tools/lib/dependency-resolver.js +26 -30
  120. package/tools/lib/yaml-utils.js +7 -7
  121. package/tools/preview-release-notes.js +66 -0
  122. package/tools/shared/bannerArt.js +3 -3
  123. package/tools/sync-installer-version.js +7 -9
  124. package/tools/update-expansion-version.js +14 -15
  125. package/tools/upgraders/v3-to-v4-upgrader.js +203 -294
  126. package/tools/version-bump.js +41 -26
  127. package/tools/yaml-format.js +56 -43
  128. package/.github/workflows/promote-to-stable.yml +0 -144
  129. package/.github/workflows/release.yaml +0 -60
  130. package/.releaserc.json +0 -21
  131. package/tools/semantic-release-sync-installer.js +0 -30
@@ -1,8 +1,6 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const { execSync } = require('child_process');
5
- const path = require('path');
1
+ const fs = require('node:fs');
2
+ const { execSync } = require('node:child_process');
3
+ const path = require('node:path');
6
4
 
7
5
  // Dynamic import for ES module
8
6
  let chalk;
@@ -26,54 +24,71 @@ function getCurrentVersion() {
26
24
 
27
25
  async function bumpVersion(type = 'patch') {
28
26
  await initializeModules();
29
-
27
+
30
28
  const validTypes = ['patch', 'minor', 'major'];
31
29
  if (!validTypes.includes(type)) {
32
30
  console.error(chalk.red(`Invalid version type: ${type}. Use: ${validTypes.join(', ')}`));
33
31
  process.exit(1);
34
32
  }
35
33
 
36
- console.log(chalk.yellow('⚠️ Manual version bumping is disabled.'));
37
- console.log(chalk.blue('🤖 This project uses semantic-release for automated versioning.'));
38
- console.log('');
39
- console.log(chalk.bold('To create a new release, use conventional commits:'));
40
- console.log(chalk.cyan(' feat: new feature (minor version bump)'));
41
- console.log(chalk.cyan(' fix: bug fix (patch version bump)'));
42
- console.log(chalk.cyan(' feat!: breaking change (major version bump)'));
43
- console.log('');
44
- console.log(chalk.dim('Example: git commit -m "feat: add new installer features"'));
45
- console.log(chalk.dim('Then push to main branch to trigger automatic release.'));
46
-
47
- return null;
34
+ const currentVersion = getCurrentVersion();
35
+ const versionParts = currentVersion.split('.').map(Number);
36
+ let newVersion;
37
+
38
+ switch (type) {
39
+ case 'major': {
40
+ newVersion = `${versionParts[0] + 1}.0.0`;
41
+ break;
42
+ }
43
+ case 'minor': {
44
+ newVersion = `${versionParts[0]}.${versionParts[1] + 1}.0`;
45
+ break;
46
+ }
47
+ case 'patch': {
48
+ newVersion = `${versionParts[0]}.${versionParts[1]}.${versionParts[2] + 1}`;
49
+ break;
50
+ }
51
+ }
52
+
53
+ console.log(chalk.blue(`Bumping version: ${currentVersion} → ${newVersion}`));
54
+
55
+ // Update package.json
56
+ const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
57
+ packageJson.version = newVersion;
58
+ fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2) + '\n');
59
+
60
+ console.log(chalk.green(`✓ Updated package.json to ${newVersion}`));
61
+
62
+ return newVersion;
48
63
  }
49
64
 
50
65
  async function main() {
51
66
  await initializeModules();
52
-
67
+
53
68
  const type = process.argv[2] || 'patch';
54
69
  const currentVersion = getCurrentVersion();
55
-
70
+
56
71
  console.log(chalk.blue(`Current version: ${currentVersion}`));
57
-
72
+
58
73
  // Check if working directory is clean
59
74
  try {
60
75
  execSync('git diff-index --quiet HEAD --');
61
- } catch (error) {
76
+ } catch {
62
77
  console.error(chalk.red('❌ Working directory is not clean. Commit your changes first.'));
63
78
  process.exit(1);
64
79
  }
65
-
80
+
66
81
  const newVersion = await bumpVersion(type);
67
-
82
+
68
83
  console.log(chalk.green(`\n🎉 Version bump complete!`));
69
84
  console.log(chalk.blue(`📦 ${currentVersion} → ${newVersion}`));
70
85
  }
71
86
 
72
87
  if (require.main === module) {
73
- main().catch(error => {
88
+ main().catch((error) => {
74
89
  console.error('Error:', error);
75
90
  process.exit(1);
76
91
  });
77
92
  }
78
93
 
79
- module.exports = { bumpVersion, getCurrentVersion };
94
+ module.exports = { bumpVersion, getCurrentVersion };
@@ -1,9 +1,7 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs');
4
- const path = require('path');
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
5
3
  const yaml = require('js-yaml');
6
- const { execSync } = require('child_process');
4
+ const { execSync } = require('node:child_process');
7
5
 
8
6
  // Dynamic import for ES module
9
7
  let chalk;
@@ -26,43 +24,50 @@ async function formatYamlContent(content, filename) {
26
24
  // First try to fix common YAML issues
27
25
  let fixedContent = content
28
26
  // Fix "commands :" -> "commands:"
29
- .replace(/^(\s*)(\w+)\s+:/gm, '$1$2:')
27
+ .replaceAll(/^(\s*)(\w+)\s+:/gm, '$1$2:')
30
28
  // Fix inconsistent list indentation
31
- .replace(/^(\s*)-\s{3,}/gm, '$1- ');
32
-
29
+ .replaceAll(/^(\s*)-\s{3,}/gm, '$1- ');
30
+
33
31
  // Skip auto-fixing for .roomodes files - they have special nested structure
34
32
  if (!filename.includes('.roomodes')) {
35
33
  fixedContent = fixedContent
36
34
  // Fix unquoted list items that contain special characters or multiple parts
37
- .replace(/^(\s*)-\s+(.*)$/gm, (match, indent, content) => {
35
+ .replaceAll(/^(\s*)-\s+(.*)$/gm, (match, indent, content) => {
38
36
  // Skip if already quoted
39
37
  if (content.startsWith('"') && content.endsWith('"')) {
40
38
  return match;
41
39
  }
42
40
  // If the content contains special YAML characters or looks complex, quote it
43
41
  // BUT skip if it looks like a proper YAML key-value pair (like "key: value")
44
- if ((content.includes(':') || content.includes('-') || content.includes('{') || content.includes('}')) &&
45
- !content.match(/^\w+:\s/)) {
42
+ if (
43
+ (content.includes(':') ||
44
+ content.includes('-') ||
45
+ content.includes('{') ||
46
+ content.includes('}')) &&
47
+ !/^\w+:\s/.test(content)
48
+ ) {
46
49
  // Remove any existing quotes first, escape internal quotes, then add proper quotes
47
- const cleanContent = content.replace(/^["']|["']$/g, '').replace(/"/g, '\\"');
50
+ const cleanContent = content
51
+ .replaceAll(/^["']|["']$/g, '')
52
+ .replaceAll('"', String.raw`\"`);
48
53
  return `${indent}- "${cleanContent}"`;
49
54
  }
50
55
  return match;
51
56
  });
52
57
  }
53
-
58
+
54
59
  // Debug: show what we're trying to parse
55
60
  if (fixedContent !== content) {
56
61
  console.log(chalk.blue(`🔧 Applied YAML fixes to ${filename}`));
57
62
  }
58
-
63
+
59
64
  // Parse and re-dump YAML to format it
60
65
  const parsed = yaml.load(fixedContent);
61
66
  const formatted = yaml.dump(parsed, {
62
67
  indent: 2,
63
68
  lineWidth: -1, // Disable line wrapping
64
69
  noRefs: true,
65
- sortKeys: false // Preserve key order
70
+ sortKeys: false, // Preserve key order
66
71
  });
67
72
  return formatted;
68
73
  } catch (error) {
@@ -80,7 +85,7 @@ async function processMarkdownFile(filePath) {
80
85
 
81
86
  // Fix untyped code blocks by adding 'text' type
82
87
  // Match ``` at start of line followed by newline, but only if it's an opening fence
83
- newContent = newContent.replace(/^```\n([\s\S]*?)\n```$/gm, '```text\n$1\n```');
88
+ newContent = newContent.replaceAll(/^```\n([\s\S]*?)\n```$/gm, '```text\n$1\n```');
84
89
  if (newContent !== content) {
85
90
  modified = true;
86
91
  console.log(chalk.blue(`🔧 Added 'text' type to untyped code blocks in ${filePath}`));
@@ -90,30 +95,30 @@ async function processMarkdownFile(filePath) {
90
95
  const yamlBlockRegex = /```ya?ml\n([\s\S]*?)\n```/g;
91
96
  let match;
92
97
  const replacements = [];
93
-
98
+
94
99
  while ((match = yamlBlockRegex.exec(newContent)) !== null) {
95
100
  const [fullMatch, yamlContent] = match;
96
101
  const formatted = await formatYamlContent(yamlContent, filePath);
97
102
  if (formatted !== null) {
98
103
  // Remove trailing newline that js-yaml adds
99
104
  const trimmedFormatted = formatted.replace(/\n$/, '');
100
-
105
+
101
106
  if (trimmedFormatted !== yamlContent) {
102
107
  modified = true;
103
108
  console.log(chalk.green(`✓ Formatted YAML in ${filePath}`));
104
109
  }
105
-
110
+
106
111
  replacements.push({
107
112
  start: match.index,
108
113
  end: match.index + fullMatch.length,
109
- replacement: `\`\`\`yaml\n${trimmedFormatted}\n\`\`\``
114
+ replacement: `\`\`\`yaml\n${trimmedFormatted}\n\`\`\``,
110
115
  });
111
116
  }
112
117
  }
113
-
118
+
114
119
  // Apply replacements in reverse order to maintain indices
115
- for (let i = replacements.length - 1; i >= 0; i--) {
116
- const { start, end, replacement } = replacements[i];
120
+ for (let index = replacements.length - 1; index >= 0; index--) {
121
+ const { start, end, replacement } = replacements[index];
117
122
  newContent = newContent.slice(0, start) + replacement + newContent.slice(end);
118
123
  }
119
124
 
@@ -128,11 +133,11 @@ async function processYamlFile(filePath) {
128
133
  await initializeModules();
129
134
  const content = fs.readFileSync(filePath, 'utf8');
130
135
  const formatted = await formatYamlContent(content, filePath);
131
-
136
+
132
137
  if (formatted === null) {
133
138
  return false; // Syntax error
134
139
  }
135
-
140
+
136
141
  if (formatted !== content) {
137
142
  fs.writeFileSync(filePath, formatted);
138
143
  return true;
@@ -155,10 +160,10 @@ async function lintYamlFile(filePath) {
155
160
 
156
161
  async function main() {
157
162
  await initializeModules();
158
- const args = process.argv.slice(2);
163
+ const arguments_ = process.argv.slice(2);
159
164
  const glob = require('glob');
160
-
161
- if (args.length === 0) {
165
+
166
+ if (arguments_.length === 0) {
162
167
  console.error('Usage: node yaml-format.js <file1> [file2] ...');
163
168
  process.exit(1);
164
169
  }
@@ -169,38 +174,44 @@ async function main() {
169
174
 
170
175
  // Expand glob patterns and collect all files
171
176
  const allFiles = [];
172
- for (const arg of args) {
173
- if (arg.includes('*')) {
177
+ for (const argument of arguments_) {
178
+ if (argument.includes('*')) {
174
179
  // It's a glob pattern
175
- const matches = glob.sync(arg);
180
+ const matches = glob.sync(argument);
176
181
  allFiles.push(...matches);
177
182
  } else {
178
183
  // It's a direct file path
179
- allFiles.push(arg);
184
+ allFiles.push(argument);
180
185
  }
181
186
  }
182
187
 
183
188
  for (const filePath of allFiles) {
184
189
  if (!fs.existsSync(filePath)) {
185
190
  // Skip silently for glob patterns that don't match anything
186
- if (!args.some(arg => arg.includes('*') && filePath === arg)) {
191
+ if (!arguments_.some((argument) => argument.includes('*') && filePath === argument)) {
187
192
  console.error(chalk.red(`❌ File not found: ${filePath}`));
188
193
  hasErrors = true;
189
194
  }
190
195
  continue;
191
196
  }
192
197
 
193
- const ext = path.extname(filePath).toLowerCase();
198
+ const extension = path.extname(filePath).toLowerCase();
194
199
  const basename = path.basename(filePath).toLowerCase();
195
-
200
+
196
201
  try {
197
202
  let changed = false;
198
- if (ext === '.md') {
203
+ if (extension === '.md') {
199
204
  changed = await processMarkdownFile(filePath);
200
- } else if (ext === '.yaml' || ext === '.yml' || basename.includes('roomodes') || basename.includes('.yaml') || basename.includes('.yml')) {
205
+ } else if (
206
+ extension === '.yaml' ||
207
+ extension === '.yml' ||
208
+ basename.includes('roomodes') ||
209
+ basename.includes('.yaml') ||
210
+ basename.includes('.yml')
211
+ ) {
201
212
  // Handle YAML files and special cases like .roomodes
202
213
  changed = await processYamlFile(filePath);
203
-
214
+
204
215
  // Also run linting
205
216
  const lintPassed = await lintYamlFile(filePath);
206
217
  if (!lintPassed) hasErrors = true;
@@ -208,7 +219,7 @@ async function main() {
208
219
  // Skip silently for unsupported files
209
220
  continue;
210
221
  }
211
-
222
+
212
223
  if (changed) {
213
224
  hasChanges = true;
214
225
  filesProcessed.push(filePath);
@@ -220,8 +231,10 @@ async function main() {
220
231
  }
221
232
 
222
233
  if (hasChanges) {
223
- console.log(chalk.green(`\n✨ YAML formatting completed! Modified ${filesProcessed.length} files:`));
224
- filesProcessed.forEach(file => console.log(chalk.blue(` 📝 ${file}`)));
234
+ console.log(
235
+ chalk.green(`\n✨ YAML formatting completed! Modified ${filesProcessed.length} files:`),
236
+ );
237
+ for (const file of filesProcessed) console.log(chalk.blue(` 📝 ${file}`));
225
238
  }
226
239
 
227
240
  if (hasErrors) {
@@ -231,10 +244,10 @@ async function main() {
231
244
  }
232
245
 
233
246
  if (require.main === module) {
234
- main().catch(error => {
247
+ main().catch((error) => {
235
248
  console.error('Error:', error);
236
249
  process.exit(1);
237
250
  });
238
251
  }
239
252
 
240
- module.exports = { formatYamlContent, processMarkdownFile, processYamlFile };
253
+ module.exports = { formatYamlContent, processMarkdownFile, processYamlFile };
@@ -1,144 +0,0 @@
1
- name: Promote to Stable
2
-
3
- on:
4
- workflow_dispatch:
5
- inputs:
6
- version_bump:
7
- description: 'Version bump type'
8
- required: true
9
- default: 'minor'
10
- type: choice
11
- options:
12
- - patch
13
- - minor
14
- - major
15
-
16
- jobs:
17
- promote:
18
- runs-on: ubuntu-latest
19
- permissions:
20
- contents: write
21
- pull-requests: write
22
-
23
- steps:
24
- - name: Checkout repository
25
- uses: actions/checkout@v4
26
- with:
27
- fetch-depth: 0
28
- token: ${{ secrets.GITHUB_TOKEN }}
29
-
30
- - name: Setup Node.js
31
- uses: actions/setup-node@v4
32
- with:
33
- node-version: '20'
34
- registry-url: 'https://registry.npmjs.org'
35
-
36
- - name: Configure Git
37
- run: |
38
- git config --global user.name "github-actions[bot]"
39
- git config --global user.email "github-actions[bot]@users.noreply.github.com"
40
- git config --global url."https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/".insteadOf "https://github.com/"
41
-
42
- - name: Switch to stable branch
43
- run: |
44
- git checkout stable
45
- git pull origin stable
46
-
47
- - name: Merge main into stable
48
- run: |
49
- git merge origin/main --no-edit
50
-
51
- - name: Install dependencies
52
- run: npm ci
53
-
54
- - name: Get current version and calculate new version
55
- id: version
56
- run: |
57
- # Get current version from package.json
58
- CURRENT_VERSION=$(node -p "require('./package.json').version")
59
- echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
60
-
61
- # Remove beta suffix if present
62
- BASE_VERSION=$(echo $CURRENT_VERSION | sed 's/-beta\.[0-9]\+//')
63
- echo "base_version=$BASE_VERSION" >> $GITHUB_OUTPUT
64
-
65
- # Calculate new version based on bump type
66
- IFS='.' read -ra VERSION_PARTS <<< "$BASE_VERSION"
67
- MAJOR=${VERSION_PARTS[0]}
68
- MINOR=${VERSION_PARTS[1]}
69
- PATCH=${VERSION_PARTS[2]}
70
-
71
- case "${{ github.event.inputs.version_bump }}" in
72
- "major")
73
- NEW_VERSION="$((MAJOR + 1)).0.0"
74
- ;;
75
- "minor")
76
- NEW_VERSION="$MAJOR.$((MINOR + 1)).0"
77
- ;;
78
- "patch")
79
- NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
80
- ;;
81
- *)
82
- NEW_VERSION="$BASE_VERSION"
83
- ;;
84
- esac
85
-
86
- # Check if calculated version already exists on NPM and increment if necessary
87
- while npm view bmad-method@$NEW_VERSION version >/dev/null 2>&1; do
88
- echo "Version $NEW_VERSION already exists, incrementing..."
89
- IFS='.' read -ra NEW_VERSION_PARTS <<< "$NEW_VERSION"
90
- NEW_MAJOR=${NEW_VERSION_PARTS[0]}
91
- NEW_MINOR=${NEW_VERSION_PARTS[1]}
92
- NEW_PATCH=${NEW_VERSION_PARTS[2]}
93
-
94
- case "${{ github.event.inputs.version_bump }}" in
95
- "major")
96
- NEW_VERSION="$((NEW_MAJOR + 1)).0.0"
97
- ;;
98
- "minor")
99
- NEW_VERSION="$NEW_MAJOR.$((NEW_MINOR + 1)).0"
100
- ;;
101
- "patch")
102
- NEW_VERSION="$NEW_MAJOR.$NEW_MINOR.$((NEW_PATCH + 1))"
103
- ;;
104
- esac
105
- done
106
-
107
- echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
108
- echo "Promoting from $CURRENT_VERSION to $NEW_VERSION"
109
-
110
- - name: Update package.json versions
111
- run: |
112
- # Update main package.json
113
- npm version ${{ steps.version.outputs.new_version }} --no-git-tag-version
114
-
115
- # Update installer package.json
116
- sed -i 's/"version": ".*"/"version": "${{ steps.version.outputs.new_version }}"/' tools/installer/package.json
117
-
118
- - name: Update package-lock.json
119
- run: npm install --package-lock-only
120
-
121
- - name: Commit stable release
122
- run: |
123
- git add .
124
- git commit -m "feat: promote to stable ${{ steps.version.outputs.new_version }}
125
-
126
- BREAKING CHANGE: Promote beta features to stable release
127
-
128
- - Update version from ${{ steps.version.outputs.current_version }} to ${{ steps.version.outputs.new_version }}
129
- - Automated promotion via GitHub Actions"
130
-
131
- - name: Push stable release
132
- run: |
133
- git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git
134
- git push origin stable
135
-
136
- - name: Switch back to main
137
- run: git checkout main
138
-
139
- - name: Summary
140
- run: |
141
- echo "🎉 Successfully promoted to stable!"
142
- echo "📦 Version: ${{ steps.version.outputs.new_version }}"
143
- echo "🚀 The stable release will be automatically published to NPM via semantic-release"
144
- echo "✅ Users running 'npx bmad-method install' will now get version ${{ steps.version.outputs.new_version }}"
@@ -1,60 +0,0 @@
1
- name: Release
2
- 'on':
3
- push:
4
- branches:
5
- - main
6
- - stable
7
- workflow_dispatch:
8
- inputs:
9
- version_type:
10
- description: Version bump type
11
- required: true
12
- default: patch
13
- type: choice
14
- options:
15
- - patch
16
- - minor
17
- - major
18
- permissions:
19
- contents: write
20
- issues: write
21
- pull-requests: write
22
- packages: write
23
- jobs:
24
- release:
25
- runs-on: ubuntu-latest
26
- if: '!contains(github.event.head_commit.message, ''[skip ci]'')'
27
- steps:
28
- - name: Checkout
29
- uses: actions/checkout@v4
30
- with:
31
- fetch-depth: 0
32
- token: ${{ secrets.GITHUB_TOKEN }}
33
- - name: Setup Node.js
34
- uses: actions/setup-node@v4
35
- with:
36
- node-version: '20'
37
- cache: npm
38
- registry-url: https://registry.npmjs.org
39
- - name: Install dependencies
40
- run: npm ci
41
- - name: Run tests and validation
42
- run: |
43
- npm run validate
44
- npm run format
45
- - name: Debug permissions
46
- run: |
47
- echo "Testing git permissions..."
48
- git config user.name "github-actions[bot]"
49
- git config user.email "github-actions[bot]@users.noreply.github.com"
50
- echo "Git config set successfully"
51
- - name: Manual version bump
52
- if: github.event_name == 'workflow_dispatch'
53
- run: npm run version:${{ github.event.inputs.version_type }}
54
- - name: Semantic Release
55
- if: github.event_name == 'push'
56
- env:
57
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58
- NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
59
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
60
- run: npm run release
package/.releaserc.json DELETED
@@ -1,21 +0,0 @@
1
- {
2
- "branches": [
3
- {
4
- "name": "main",
5
- "prerelease": "beta",
6
- "channel": "beta"
7
- },
8
- {
9
- "name": "stable",
10
- "channel": "latest"
11
- }
12
- ],
13
- "plugins": [
14
- "@semantic-release/commit-analyzer",
15
- "@semantic-release/release-notes-generator",
16
- "@semantic-release/changelog",
17
- "@semantic-release/npm",
18
- "./tools/semantic-release-sync-installer.js",
19
- "@semantic-release/github"
20
- ]
21
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * Semantic-release plugin to sync installer package.json version
3
- */
4
-
5
- const fs = require('fs');
6
- const path = require('path');
7
-
8
- // This function runs during the "prepare" step of semantic-release
9
- function prepare(_, { nextRelease, logger }) {
10
- // Define the path to the installer package.json file
11
- const file = path.join(process.cwd(), 'tools/installer/package.json');
12
-
13
- // If the file does not exist, skip syncing and log a message
14
- if (!fs.existsSync(file)) return logger.log('Installer package.json not found, skipping');
15
-
16
- // Read and parse the package.json file
17
- const pkg = JSON.parse(fs.readFileSync(file, 'utf8'));
18
-
19
- // Update the version field with the next release version
20
- pkg.version = nextRelease.version;
21
-
22
- // Write the updated JSON back to the file
23
- fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + '\n');
24
-
25
- // Log success message
26
- logger.log(`Synced installer package.json to version ${nextRelease.version}`);
27
- }
28
-
29
- // Export the prepare function so semantic-release can use it
30
- module.exports = { prepare };