bmad-fh 6.0.0-alpha.23 → 6.0.0-alpha.23.3b00cb36
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.
- package/.github/workflows/publish.yaml +68 -0
- package/.husky/pre-commit +17 -2
- package/.husky/pre-push +10 -0
- package/README.md +117 -14
- package/eslint.config.mjs +2 -2
- package/package.json +1 -2
- package/src/bmm/module.yaml +2 -2
- package/src/core/lib/scope/artifact-resolver.js +26 -26
- package/src/core/lib/scope/event-logger.js +34 -45
- package/src/core/lib/scope/index.js +3 -3
- package/src/core/lib/scope/scope-context.js +22 -28
- package/src/core/lib/scope/scope-initializer.js +29 -31
- package/src/core/lib/scope/scope-manager.js +57 -24
- package/src/core/lib/scope/scope-migrator.js +44 -52
- package/src/core/lib/scope/scope-sync.js +42 -48
- package/src/core/lib/scope/scope-validator.js +16 -21
- package/src/core/lib/scope/state-lock.js +37 -43
- package/src/core/module.yaml +2 -2
- package/test/test-scope-cli.js +1306 -0
- package/test/test-scope-e2e.js +682 -92
- package/test/test-scope-system.js +973 -169
- package/tools/cli/bmad-cli.js +5 -0
- package/tools/cli/commands/scope.js +1250 -115
- package/tools/cli/installers/lib/modules/manager.js +6 -2
- package/tools/cli/scripts/migrate-workflows.js +43 -51
- package/.github/workflows/publish-multi-artifact.yaml +0 -50
|
@@ -416,7 +416,9 @@ class ModuleManager {
|
|
|
416
416
|
if (needsDependencyInstall || wasNewClone || nodeModulesMissing) {
|
|
417
417
|
const installSpinner = ora(`Installing dependencies for ${moduleInfo.name}...`).start();
|
|
418
418
|
try {
|
|
419
|
-
|
|
419
|
+
// Remove lockfile first - it may reference devDeps that don't exist
|
|
420
|
+
execSync('rm -f package-lock.json', { cwd: moduleCacheDir, stdio: 'pipe' });
|
|
421
|
+
execSync('npm install --omit=dev --ignore-scripts --no-package-lock --no-audit --no-fund --prefer-offline --no-progress', {
|
|
420
422
|
cwd: moduleCacheDir,
|
|
421
423
|
stdio: 'pipe',
|
|
422
424
|
timeout: 120_000, // 2 minute timeout
|
|
@@ -441,7 +443,9 @@ class ModuleManager {
|
|
|
441
443
|
if (packageJsonNewer) {
|
|
442
444
|
const installSpinner = ora(`Installing dependencies for ${moduleInfo.name}...`).start();
|
|
443
445
|
try {
|
|
444
|
-
|
|
446
|
+
// Remove lockfile first - it may reference devDeps that don't exist
|
|
447
|
+
execSync('rm -f package-lock.json', { cwd: moduleCacheDir, stdio: 'pipe' });
|
|
448
|
+
execSync('npm install --omit=dev --ignore-scripts --no-package-lock --no-audit --no-fund --prefer-offline --no-progress', {
|
|
445
449
|
cwd: moduleCacheDir,
|
|
446
450
|
stdio: 'pipe',
|
|
447
451
|
timeout: 120_000, // 2 minute timeout
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
|
|
2
1
|
/**
|
|
3
2
|
* Workflow Migration Script
|
|
4
|
-
*
|
|
3
|
+
*
|
|
5
4
|
* Updates workflow.yaml files to support the multi-scope system.
|
|
6
5
|
* Primarily updates test_dir and other path variables to use scope-aware paths.
|
|
7
|
-
*
|
|
6
|
+
*
|
|
8
7
|
* Usage:
|
|
9
8
|
* node migrate-workflows.js [--dry-run] [--verbose]
|
|
10
|
-
*
|
|
9
|
+
*
|
|
11
10
|
* Options:
|
|
12
11
|
* --dry-run Show what would be changed without making changes
|
|
13
12
|
* --verbose Show detailed output
|
|
@@ -28,48 +27,42 @@ const PATH_MIGRATIONS = [
|
|
|
28
27
|
{
|
|
29
28
|
pattern: /\{output_folder\}\/tests/g,
|
|
30
29
|
replacement: '{scope_tests}',
|
|
31
|
-
description: 'test directory to scope_tests'
|
|
30
|
+
description: 'test directory to scope_tests',
|
|
32
31
|
},
|
|
33
32
|
{
|
|
34
33
|
pattern: /\{config_source:implementation_artifacts\}\/tests/g,
|
|
35
34
|
replacement: '{config_source:scope_tests}',
|
|
36
|
-
description: 'implementation_artifacts tests to scope_tests'
|
|
35
|
+
description: 'implementation_artifacts tests to scope_tests',
|
|
37
36
|
},
|
|
38
37
|
// Planning artifacts
|
|
39
38
|
{
|
|
40
39
|
pattern: /\{output_folder\}\/planning-artifacts/g,
|
|
41
40
|
replacement: '{config_source:planning_artifacts}',
|
|
42
|
-
description: 'output_folder planning to config_source'
|
|
41
|
+
description: 'output_folder planning to config_source',
|
|
43
42
|
},
|
|
44
|
-
// Implementation artifacts
|
|
43
|
+
// Implementation artifacts
|
|
45
44
|
{
|
|
46
45
|
pattern: /\{output_folder\}\/implementation-artifacts/g,
|
|
47
46
|
replacement: '{config_source:implementation_artifacts}',
|
|
48
|
-
description: 'output_folder implementation to config_source'
|
|
49
|
-
}
|
|
47
|
+
description: 'output_folder implementation to config_source',
|
|
48
|
+
},
|
|
50
49
|
];
|
|
51
50
|
|
|
52
51
|
// Variables that indicate scope requirement
|
|
53
|
-
const SCOPE_INDICATORS = [
|
|
54
|
-
'{scope}',
|
|
55
|
-
'{scope_path}',
|
|
56
|
-
'{scope_tests}',
|
|
57
|
-
'{scope_planning}',
|
|
58
|
-
'{scope_implementation}'
|
|
59
|
-
];
|
|
52
|
+
const SCOPE_INDICATORS = ['{scope}', '{scope_path}', '{scope_tests}', '{scope_planning}', '{scope_implementation}'];
|
|
60
53
|
|
|
61
54
|
/**
|
|
62
55
|
* Find all workflow.yaml files
|
|
63
56
|
*/
|
|
64
57
|
async function findWorkflowFiles(basePath) {
|
|
65
58
|
const files = [];
|
|
66
|
-
|
|
59
|
+
|
|
67
60
|
async function walk(dir) {
|
|
68
61
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
69
|
-
|
|
62
|
+
|
|
70
63
|
for (const entry of entries) {
|
|
71
64
|
const fullPath = path.join(dir, entry.name);
|
|
72
|
-
|
|
65
|
+
|
|
73
66
|
if (entry.isDirectory()) {
|
|
74
67
|
// Skip node_modules and hidden directories
|
|
75
68
|
if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
@@ -80,7 +73,7 @@ async function findWorkflowFiles(basePath) {
|
|
|
80
73
|
}
|
|
81
74
|
}
|
|
82
75
|
}
|
|
83
|
-
|
|
76
|
+
|
|
84
77
|
await walk(basePath);
|
|
85
78
|
return files;
|
|
86
79
|
}
|
|
@@ -89,7 +82,7 @@ async function findWorkflowFiles(basePath) {
|
|
|
89
82
|
* Check if a workflow already uses scope variables
|
|
90
83
|
*/
|
|
91
84
|
function usesScope(content) {
|
|
92
|
-
return SCOPE_INDICATORS.some(indicator => content.includes(indicator));
|
|
85
|
+
return SCOPE_INDICATORS.some((indicator) => content.includes(indicator));
|
|
93
86
|
}
|
|
94
87
|
|
|
95
88
|
/**
|
|
@@ -101,20 +94,20 @@ function analyzeWorkflow(content, filePath) {
|
|
|
101
94
|
needsMigration: false,
|
|
102
95
|
alreadyScoped: false,
|
|
103
96
|
suggestions: [],
|
|
104
|
-
currentVariables: []
|
|
97
|
+
currentVariables: [],
|
|
105
98
|
};
|
|
106
|
-
|
|
99
|
+
|
|
107
100
|
// Check if already uses scope
|
|
108
101
|
if (usesScope(content)) {
|
|
109
102
|
analysis.alreadyScoped = true;
|
|
110
103
|
return analysis;
|
|
111
104
|
}
|
|
112
|
-
|
|
105
|
+
|
|
113
106
|
// Find variables that might need migration
|
|
114
107
|
const variablePattern = /\{[^}]+\}/g;
|
|
115
108
|
const matches = content.match(variablePattern) || [];
|
|
116
109
|
analysis.currentVariables = [...new Set(matches)];
|
|
117
|
-
|
|
110
|
+
|
|
118
111
|
// Check each migration pattern
|
|
119
112
|
for (const migration of PATH_MIGRATIONS) {
|
|
120
113
|
if (migration.pattern.test(content)) {
|
|
@@ -122,21 +115,21 @@ function analyzeWorkflow(content, filePath) {
|
|
|
122
115
|
analysis.suggestions.push({
|
|
123
116
|
description: migration.description,
|
|
124
117
|
pattern: migration.pattern.toString(),
|
|
125
|
-
replacement: migration.replacement
|
|
118
|
+
replacement: migration.replacement,
|
|
126
119
|
});
|
|
127
120
|
}
|
|
128
121
|
}
|
|
129
|
-
|
|
122
|
+
|
|
130
123
|
// Check for test_dir variable
|
|
131
124
|
if (content.includes('test_dir:') || content.includes('test_dir:')) {
|
|
132
125
|
analysis.needsMigration = true;
|
|
133
126
|
analysis.suggestions.push({
|
|
134
127
|
description: 'Has test_dir variable - may need scope_tests',
|
|
135
128
|
pattern: 'test_dir',
|
|
136
|
-
replacement: 'scope_tests via config_source'
|
|
129
|
+
replacement: 'scope_tests via config_source',
|
|
137
130
|
});
|
|
138
131
|
}
|
|
139
|
-
|
|
132
|
+
|
|
140
133
|
return analysis;
|
|
141
134
|
}
|
|
142
135
|
|
|
@@ -146,14 +139,14 @@ function analyzeWorkflow(content, filePath) {
|
|
|
146
139
|
function migrateWorkflow(content) {
|
|
147
140
|
let migrated = content;
|
|
148
141
|
let changes = [];
|
|
149
|
-
|
|
142
|
+
|
|
150
143
|
for (const migration of PATH_MIGRATIONS) {
|
|
151
144
|
if (migration.pattern.test(migrated)) {
|
|
152
145
|
migrated = migrated.replace(migration.pattern, migration.replacement);
|
|
153
146
|
changes.push(migration.description);
|
|
154
147
|
}
|
|
155
148
|
}
|
|
156
|
-
|
|
149
|
+
|
|
157
150
|
return { content: migrated, changes };
|
|
158
151
|
}
|
|
159
152
|
|
|
@@ -163,12 +156,12 @@ function migrateWorkflow(content) {
|
|
|
163
156
|
function addScopeMarker(content) {
|
|
164
157
|
try {
|
|
165
158
|
const parsed = yaml.parse(content);
|
|
166
|
-
|
|
159
|
+
|
|
167
160
|
// Add scope_required if not present
|
|
168
161
|
if (!parsed.scope_required) {
|
|
169
162
|
parsed.scope_required = false; // Default to false for backward compatibility
|
|
170
163
|
}
|
|
171
|
-
|
|
164
|
+
|
|
172
165
|
return yaml.stringify(parsed, { lineWidth: 120 });
|
|
173
166
|
} catch {
|
|
174
167
|
// If YAML parsing fails, return original
|
|
@@ -183,37 +176,37 @@ async function main() {
|
|
|
183
176
|
const args = new Set(process.argv.slice(2));
|
|
184
177
|
const dryRun = args.has('--dry-run');
|
|
185
178
|
const verbose = args.has('--verbose');
|
|
186
|
-
|
|
179
|
+
|
|
187
180
|
console.log(chalk.bold('\nWorkflow Migration Script'));
|
|
188
181
|
console.log(chalk.dim('Updating workflow.yaml files for multi-scope support\n'));
|
|
189
|
-
|
|
182
|
+
|
|
190
183
|
if (dryRun) {
|
|
191
184
|
console.log(chalk.yellow('DRY RUN MODE - No changes will be made\n'));
|
|
192
185
|
}
|
|
193
|
-
|
|
186
|
+
|
|
194
187
|
// Find all workflow files
|
|
195
188
|
console.log(chalk.blue('Scanning for workflow.yaml files...'));
|
|
196
189
|
const files = await findWorkflowFiles(SRC_PATH);
|
|
197
190
|
console.log(chalk.green(`Found ${files.length} workflow.yaml files\n`));
|
|
198
|
-
|
|
191
|
+
|
|
199
192
|
// Analysis results
|
|
200
193
|
const results = {
|
|
201
194
|
analyzed: 0,
|
|
202
195
|
alreadyScoped: 0,
|
|
203
196
|
migrated: 0,
|
|
204
197
|
noChanges: 0,
|
|
205
|
-
errors: []
|
|
198
|
+
errors: [],
|
|
206
199
|
};
|
|
207
|
-
|
|
200
|
+
|
|
208
201
|
// Process each file
|
|
209
202
|
for (const filePath of files) {
|
|
210
203
|
const relativePath = path.relative(SRC_PATH, filePath);
|
|
211
204
|
results.analyzed++;
|
|
212
|
-
|
|
205
|
+
|
|
213
206
|
try {
|
|
214
207
|
const content = await fs.readFile(filePath, 'utf8');
|
|
215
208
|
const analysis = analyzeWorkflow(content, filePath);
|
|
216
|
-
|
|
209
|
+
|
|
217
210
|
if (analysis.alreadyScoped) {
|
|
218
211
|
results.alreadyScoped++;
|
|
219
212
|
if (verbose) {
|
|
@@ -221,7 +214,7 @@ async function main() {
|
|
|
221
214
|
}
|
|
222
215
|
continue;
|
|
223
216
|
}
|
|
224
|
-
|
|
217
|
+
|
|
225
218
|
if (!analysis.needsMigration) {
|
|
226
219
|
results.noChanges++;
|
|
227
220
|
if (verbose) {
|
|
@@ -229,16 +222,16 @@ async function main() {
|
|
|
229
222
|
}
|
|
230
223
|
continue;
|
|
231
224
|
}
|
|
232
|
-
|
|
225
|
+
|
|
233
226
|
// Apply migration
|
|
234
227
|
const { content: migrated, changes } = migrateWorkflow(content);
|
|
235
|
-
|
|
228
|
+
|
|
236
229
|
if (changes.length > 0) {
|
|
237
230
|
console.log(chalk.cyan(` ● ${relativePath}`));
|
|
238
231
|
for (const change of changes) {
|
|
239
232
|
console.log(chalk.dim(` → ${change}`));
|
|
240
233
|
}
|
|
241
|
-
|
|
234
|
+
|
|
242
235
|
if (!dryRun) {
|
|
243
236
|
await fs.writeFile(filePath, migrated, 'utf8');
|
|
244
237
|
}
|
|
@@ -246,13 +239,12 @@ async function main() {
|
|
|
246
239
|
} else {
|
|
247
240
|
results.noChanges++;
|
|
248
241
|
}
|
|
249
|
-
|
|
250
242
|
} catch (error) {
|
|
251
243
|
results.errors.push({ file: relativePath, error: error.message });
|
|
252
244
|
console.log(chalk.red(` ✗ ${relativePath} - Error: ${error.message}`));
|
|
253
245
|
}
|
|
254
246
|
}
|
|
255
|
-
|
|
247
|
+
|
|
256
248
|
// Print summary
|
|
257
249
|
console.log(chalk.bold('\n─────────────────────────────────────'));
|
|
258
250
|
console.log(chalk.bold('Summary'));
|
|
@@ -265,17 +257,17 @@ async function main() {
|
|
|
265
257
|
console.log(chalk.red(` Errors: ${results.errors.length}`));
|
|
266
258
|
}
|
|
267
259
|
console.log();
|
|
268
|
-
|
|
260
|
+
|
|
269
261
|
if (dryRun && results.migrated > 0) {
|
|
270
262
|
console.log(chalk.yellow('Run without --dry-run to apply changes\n'));
|
|
271
263
|
}
|
|
272
|
-
|
|
264
|
+
|
|
273
265
|
// Exit with error code if there were errors
|
|
274
266
|
process.exit(results.errors.length > 0 ? 1 : 0);
|
|
275
267
|
}
|
|
276
268
|
|
|
277
269
|
// Run
|
|
278
|
-
main().catch(error => {
|
|
270
|
+
main().catch((error) => {
|
|
279
271
|
console.error(chalk.red('Fatal error:'), error.message);
|
|
280
272
|
process.exit(1);
|
|
281
273
|
});
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
name: Publish Multi-Artifact
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- feat/multi-artifact-support
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: read
|
|
10
|
-
packages: write
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
publish:
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
steps:
|
|
16
|
-
- name: Checkout
|
|
17
|
-
uses: actions/checkout@v4
|
|
18
|
-
with:
|
|
19
|
-
fetch-depth: 0
|
|
20
|
-
|
|
21
|
-
- name: Setup Node.js
|
|
22
|
-
uses: actions/setup-node@v4
|
|
23
|
-
with:
|
|
24
|
-
node-version-file: ".nvmrc"
|
|
25
|
-
cache: npm
|
|
26
|
-
registry-url: https://registry.npmjs.org
|
|
27
|
-
|
|
28
|
-
- name: Install dependencies
|
|
29
|
-
run: npm ci
|
|
30
|
-
|
|
31
|
-
- name: Get version
|
|
32
|
-
id: version
|
|
33
|
-
run: |
|
|
34
|
-
echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
|
|
35
|
-
|
|
36
|
-
- name: Publish to NPM
|
|
37
|
-
env:
|
|
38
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
39
|
-
run: |
|
|
40
|
-
echo "Publishing bmad-fh@${{ steps.version.outputs.version }} with tag multi-artifact"
|
|
41
|
-
npm publish --tag multi-artifact
|
|
42
|
-
|
|
43
|
-
- name: Summary
|
|
44
|
-
run: |
|
|
45
|
-
echo "## Published bmad-fh@${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY
|
|
46
|
-
echo "" >> $GITHUB_STEP_SUMMARY
|
|
47
|
-
echo "### Installation" >> $GITHUB_STEP_SUMMARY
|
|
48
|
-
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
|
|
49
|
-
echo "npx bmad-fh@multi-artifact install" >> $GITHUB_STEP_SUMMARY
|
|
50
|
-
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|