bmad-fh 6.0.0-alpha.23.02a963fa → 6.0.0-alpha.23.599980af
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/README.md +116 -13
- package/package.json +1 -1
- package/src/core/lib/scope/scope-manager.js +37 -4
- package/test/test-scope-cli.js +1306 -0
- package/test/test-scope-e2e.js +13 -17
- package/test/test-scope-system.js +115 -95
- package/tools/cli/commands/scope.js +1135 -43
- package/tools/cli/installers/lib/modules/manager.js +6 -2
|
@@ -9,6 +9,7 @@ const { select, text, confirm, isCancel } = require('../lib/prompts');
|
|
|
9
9
|
const { ScopeManager } = require('../../../src/core/lib/scope/scope-manager');
|
|
10
10
|
const { ScopeInitializer } = require('../../../src/core/lib/scope/scope-initializer');
|
|
11
11
|
const { ScopeValidator } = require('../../../src/core/lib/scope/scope-validator');
|
|
12
|
+
const { ScopeSync } = require('../../../src/core/lib/scope/scope-sync');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Format a date string for display
|
|
@@ -27,7 +28,7 @@ function formatDate(dateStr) {
|
|
|
27
28
|
*/
|
|
28
29
|
function displayScopeList(scopes) {
|
|
29
30
|
if (scopes.length === 0) {
|
|
30
|
-
console.log(chalk.yellow('\nNo scopes found. Create one with: bmad scope create <id>\n'));
|
|
31
|
+
console.log(chalk.yellow('\nNo scopes found. Create one with: npx bmad-fh@multi-artifact scope create <id>\n'));
|
|
31
32
|
return;
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -113,6 +114,16 @@ function displayScopeInfo(scope, paths, tree) {
|
|
|
113
114
|
*/
|
|
114
115
|
async function handleList(projectRoot, options) {
|
|
115
116
|
const manager = new ScopeManager({ projectRoot });
|
|
117
|
+
const initializer = new ScopeInitializer({ projectRoot });
|
|
118
|
+
|
|
119
|
+
// Check if system is initialized before trying to list
|
|
120
|
+
const isInitialized = await initializer.isSystemInitialized();
|
|
121
|
+
const configExists = await fs.pathExists(path.join(projectRoot, '_bmad', '_config', 'scopes.yaml'));
|
|
122
|
+
|
|
123
|
+
if (!isInitialized || !configExists) {
|
|
124
|
+
console.log(chalk.yellow('\nScope system not initialized. Run: npx bmad-fh@multi-artifact scope init\n'));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
116
127
|
|
|
117
128
|
try {
|
|
118
129
|
await manager.initialize();
|
|
@@ -120,7 +131,7 @@ async function handleList(projectRoot, options) {
|
|
|
120
131
|
displayScopeList(scopes);
|
|
121
132
|
} catch (error) {
|
|
122
133
|
if (error.message.includes('does not exist')) {
|
|
123
|
-
console.log(chalk.yellow('\nScope system not initialized. Run: bmad scope init\n'));
|
|
134
|
+
console.log(chalk.yellow('\nScope system not initialized. Run: npx bmad-fh@multi-artifact scope init\n'));
|
|
124
135
|
} else {
|
|
125
136
|
throw error;
|
|
126
137
|
}
|
|
@@ -176,9 +187,9 @@ async function handleCreate(projectRoot, scopeId, options) {
|
|
|
176
187
|
name = inputName;
|
|
177
188
|
}
|
|
178
189
|
|
|
179
|
-
// Get description if not provided
|
|
190
|
+
// Get description if not provided (check for undefined specifically since empty string is valid)
|
|
180
191
|
let description = options.description;
|
|
181
|
-
if (
|
|
192
|
+
if (description === undefined) {
|
|
182
193
|
const inputDesc = await text({
|
|
183
194
|
message: 'Enter scope description (optional):',
|
|
184
195
|
placeholder: 'Brief description of this scope',
|
|
@@ -203,27 +214,25 @@ async function handleCreate(projectRoot, scopeId, options) {
|
|
|
203
214
|
await initializer.initializeScopeSystem();
|
|
204
215
|
}
|
|
205
216
|
|
|
206
|
-
// Create scope in configuration
|
|
217
|
+
// Create scope in configuration and directory structure
|
|
218
|
+
// Note: manager.createScope() now also calls initializer.initializeScope() internally
|
|
207
219
|
const scope = await manager.createScope(scopeId, {
|
|
208
220
|
name,
|
|
209
221
|
description,
|
|
210
222
|
dependencies: options.dependencies ? options.dependencies.split(',').map((d) => d.trim()) : [],
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// Create scope directory structure
|
|
214
|
-
const paths = await initializer.initializeScope(scopeId, {
|
|
215
|
-
name,
|
|
216
|
-
description,
|
|
217
223
|
createContext: options.context,
|
|
218
224
|
});
|
|
219
225
|
|
|
226
|
+
// Get paths for display
|
|
227
|
+
const paths = initializer.getScopePaths(scopeId);
|
|
228
|
+
|
|
220
229
|
console.log(chalk.green(`\n✓ Scope '${scopeId}' created successfully!\n`));
|
|
221
230
|
console.log(chalk.dim(' Directories created:'));
|
|
222
231
|
console.log(` ${paths.planning}`);
|
|
223
232
|
console.log(` ${paths.implementation}`);
|
|
224
233
|
console.log(` ${paths.tests}`);
|
|
225
234
|
console.log();
|
|
226
|
-
console.log(chalk.cyan(` Use with
|
|
235
|
+
console.log(chalk.cyan(` Use with workflows by setting .bmad-scope or using BMAD_SCOPE=${scopeId}`));
|
|
227
236
|
console.log();
|
|
228
237
|
}
|
|
229
238
|
|
|
@@ -232,7 +241,7 @@ async function handleCreate(projectRoot, scopeId, options) {
|
|
|
232
241
|
*/
|
|
233
242
|
async function handleInfo(projectRoot, scopeId) {
|
|
234
243
|
if (!scopeId) {
|
|
235
|
-
console.error(chalk.red('Error: Scope ID is required. Usage: bmad scope info <scope-id>'));
|
|
244
|
+
console.error(chalk.red('Error: Scope ID is required. Usage: npx bmad-fh@multi-artifact scope info <scope-id>'));
|
|
236
245
|
process.exit(1);
|
|
237
246
|
}
|
|
238
247
|
|
|
@@ -257,7 +266,7 @@ async function handleInfo(projectRoot, scopeId) {
|
|
|
257
266
|
*/
|
|
258
267
|
async function handleRemove(projectRoot, scopeId, options) {
|
|
259
268
|
if (!scopeId) {
|
|
260
|
-
console.error(chalk.red('Error: Scope ID is required. Usage: bmad scope remove <scope-id>'));
|
|
269
|
+
console.error(chalk.red('Error: Scope ID is required. Usage: npx bmad-fh@multi-artifact scope remove <scope-id>'));
|
|
261
270
|
process.exit(1);
|
|
262
271
|
}
|
|
263
272
|
|
|
@@ -288,13 +297,15 @@ async function handleRemove(projectRoot, scopeId, options) {
|
|
|
288
297
|
console.log(chalk.blue('\nRemoving scope...'));
|
|
289
298
|
|
|
290
299
|
// Remove scope directory (with backup)
|
|
291
|
-
|
|
300
|
+
// Note: Commander.js sets options.backup to false when --no-backup is passed
|
|
301
|
+
const shouldBackup = options.backup !== false;
|
|
302
|
+
await initializer.removeScope(scopeId, { backup: shouldBackup });
|
|
292
303
|
|
|
293
304
|
// Remove from configuration
|
|
294
305
|
await manager.removeScope(scopeId, { force: true });
|
|
295
306
|
|
|
296
307
|
console.log(chalk.green(`\n✓ Scope '${scopeId}' removed successfully!`));
|
|
297
|
-
if (
|
|
308
|
+
if (shouldBackup) {
|
|
298
309
|
console.log(chalk.dim(' A backup was created in _bmad-output/'));
|
|
299
310
|
}
|
|
300
311
|
console.log();
|
|
@@ -318,7 +329,7 @@ async function handleInit(projectRoot) {
|
|
|
318
329
|
console.log(` ${chalk.cyan('_bmad-output/_shared/')} - Shared knowledge layer`);
|
|
319
330
|
console.log(` ${chalk.cyan('_bmad/_events/')} - Event system`);
|
|
320
331
|
console.log();
|
|
321
|
-
console.log(chalk.cyan(' Next: bmad scope create <scope-id>'));
|
|
332
|
+
console.log(chalk.cyan(' Next: npx bmad-fh@multi-artifact scope create <scope-id>'));
|
|
322
333
|
console.log();
|
|
323
334
|
}
|
|
324
335
|
|
|
@@ -327,7 +338,7 @@ async function handleInit(projectRoot) {
|
|
|
327
338
|
*/
|
|
328
339
|
async function handleArchive(projectRoot, scopeId) {
|
|
329
340
|
if (!scopeId) {
|
|
330
|
-
console.error(chalk.red('Error: Scope ID is required. Usage: bmad scope archive <scope-id>'));
|
|
341
|
+
console.error(chalk.red('Error: Scope ID is required. Usage: npx bmad-fh@multi-artifact scope archive <scope-id>'));
|
|
331
342
|
process.exit(1);
|
|
332
343
|
}
|
|
333
344
|
|
|
@@ -344,7 +355,7 @@ async function handleArchive(projectRoot, scopeId) {
|
|
|
344
355
|
*/
|
|
345
356
|
async function handleActivate(projectRoot, scopeId) {
|
|
346
357
|
if (!scopeId) {
|
|
347
|
-
console.error(chalk.red('Error: Scope ID is required. Usage: bmad scope activate <scope-id>'));
|
|
358
|
+
console.error(chalk.red('Error: Scope ID is required. Usage: npx bmad-fh@multi-artifact scope activate <scope-id>'));
|
|
348
359
|
process.exit(1);
|
|
349
360
|
}
|
|
350
361
|
|
|
@@ -357,34 +368,1080 @@ async function handleActivate(projectRoot, scopeId) {
|
|
|
357
368
|
}
|
|
358
369
|
|
|
359
370
|
/**
|
|
360
|
-
*
|
|
371
|
+
* Handle 'sync-up' subcommand - Promote scope artifacts to shared layer
|
|
372
|
+
*/
|
|
373
|
+
async function handleSyncUp(projectRoot, scopeId, options) {
|
|
374
|
+
if (!scopeId) {
|
|
375
|
+
console.error(chalk.red('Error: Scope ID is required. Usage: npx bmad-fh@multi-artifact scope sync-up <scope-id>'));
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const manager = new ScopeManager({ projectRoot });
|
|
380
|
+
const sync = new ScopeSync({ projectRoot });
|
|
381
|
+
|
|
382
|
+
await manager.initialize();
|
|
383
|
+
|
|
384
|
+
// Verify scope exists
|
|
385
|
+
const scope = await manager.getScope(scopeId);
|
|
386
|
+
if (!scope) {
|
|
387
|
+
console.error(chalk.red(`Error: Scope '${scopeId}' not found.`));
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Handle dry-run mode
|
|
392
|
+
if (options.dryRun) {
|
|
393
|
+
console.log(chalk.blue(`\n[Dry Run] Analyzing artifacts in '${scopeId}' for promotion...`));
|
|
394
|
+
|
|
395
|
+
// Get sync status to show what would be promoted
|
|
396
|
+
const scopePath = path.join(projectRoot, '_bmad-output', scopeId);
|
|
397
|
+
const promotablePatterns = ['architecture/*.md', 'contracts/*.md', 'principles/*.md', 'project-context.md'];
|
|
398
|
+
|
|
399
|
+
console.log(chalk.yellow('\n Would promote files matching these patterns:\n'));
|
|
400
|
+
for (const pattern of promotablePatterns) {
|
|
401
|
+
console.log(` ${chalk.cyan('•')} ${pattern}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
const status = await sync.getSyncStatus(scopeId);
|
|
406
|
+
if (status.promotedCount > 0) {
|
|
407
|
+
console.log(chalk.dim(`\n Previously promoted: ${status.promotedCount} files`));
|
|
408
|
+
for (const file of status.promotedFiles) {
|
|
409
|
+
console.log(` ${chalk.dim('✓')} ${file}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
} catch {
|
|
413
|
+
// Ignore errors getting status
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
console.log(chalk.dim('\n Run without --dry-run to execute.\n'));
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
console.log(chalk.blue(`\nPromoting artifacts from '${scopeId}' to shared layer...`));
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
// syncUp signature: syncUp(scopeId, files = null, options = {})
|
|
424
|
+
const syncOptions = {
|
|
425
|
+
force: options.resolution === 'keep-local' ? false : true,
|
|
426
|
+
};
|
|
427
|
+
const result = await sync.syncUp(scopeId, null, syncOptions);
|
|
428
|
+
|
|
429
|
+
if (result.success) {
|
|
430
|
+
console.log(chalk.green('\n✓ Sync-up complete!\n'));
|
|
431
|
+
} else {
|
|
432
|
+
console.log(chalk.yellow('\n⚠ Sync-up completed with issues.\n'));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Handle promoted files - result.promoted is array of { file, target }
|
|
436
|
+
if (result.promoted && result.promoted.length > 0) {
|
|
437
|
+
console.log(chalk.dim(' Promoted files:'));
|
|
438
|
+
for (const item of result.promoted) {
|
|
439
|
+
const displayFile = typeof item === 'string' ? item : item.file;
|
|
440
|
+
console.log(` ${chalk.cyan('→')} ${displayFile}`);
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
console.log(chalk.dim(' No files to promote (already in sync or no promotable artifacts).'));
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Handle skipped files - result.skipped is array of { file, reason }
|
|
447
|
+
if (result.skipped && result.skipped.length > 0) {
|
|
448
|
+
console.log(chalk.dim('\n Skipped files:'));
|
|
449
|
+
for (const item of result.skipped) {
|
|
450
|
+
const file = typeof item === 'string' ? item : item.file;
|
|
451
|
+
const reason = typeof item === 'object' ? item.reason : 'unknown';
|
|
452
|
+
console.log(` ${chalk.yellow('○')} ${file} - ${reason}`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Handle conflicts - result.conflicts is array of { file, source, target, resolution }
|
|
457
|
+
if (result.conflicts && result.conflicts.length > 0) {
|
|
458
|
+
console.log(chalk.yellow('\n Conflicts detected:'));
|
|
459
|
+
for (const conflict of result.conflicts) {
|
|
460
|
+
const file = typeof conflict === 'string' ? conflict : conflict.file;
|
|
461
|
+
console.log(` ${chalk.yellow('!')} ${file}`);
|
|
462
|
+
|
|
463
|
+
// Attempt to resolve conflict if resolution strategy provided
|
|
464
|
+
if (options.resolution && options.resolution !== 'prompt') {
|
|
465
|
+
const resolveResult = await sync.resolveConflict(conflict, options.resolution);
|
|
466
|
+
if (resolveResult.success) {
|
|
467
|
+
console.log(` ${chalk.green('✓')} Resolved: ${resolveResult.action}`);
|
|
468
|
+
} else {
|
|
469
|
+
console.log(` ${chalk.red('✗')} Failed: ${resolveResult.error}`);
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
console.log(` ${chalk.dim('Use --resolution to auto-resolve')}`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Handle errors - result.errors is array of { file, error } or { error }
|
|
478
|
+
if (result.errors && result.errors.length > 0) {
|
|
479
|
+
console.log(chalk.red('\n Errors:'));
|
|
480
|
+
for (const err of result.errors) {
|
|
481
|
+
if (err.file) {
|
|
482
|
+
console.log(` ${chalk.red('✗')} ${err.file}: ${err.error}`);
|
|
483
|
+
} else {
|
|
484
|
+
console.log(` ${chalk.red('✗')} ${err.error}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
console.log();
|
|
490
|
+
} catch (error) {
|
|
491
|
+
console.error(chalk.red(`\nSync-up failed: ${error.message}`));
|
|
492
|
+
if (process.env.DEBUG) {
|
|
493
|
+
console.error(chalk.dim(error.stack));
|
|
494
|
+
}
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Handle 'sync-down' subcommand - Pull shared layer updates into scope
|
|
501
|
+
*/
|
|
502
|
+
async function handleSyncDown(projectRoot, scopeId, options) {
|
|
503
|
+
if (!scopeId) {
|
|
504
|
+
console.error(chalk.red('Error: Scope ID is required. Usage: npx bmad-fh@multi-artifact scope sync-down <scope-id>'));
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const manager = new ScopeManager({ projectRoot });
|
|
509
|
+
const sync = new ScopeSync({ projectRoot });
|
|
510
|
+
|
|
511
|
+
await manager.initialize();
|
|
512
|
+
|
|
513
|
+
// Verify scope exists
|
|
514
|
+
const scope = await manager.getScope(scopeId);
|
|
515
|
+
if (!scope) {
|
|
516
|
+
console.error(chalk.red(`Error: Scope '${scopeId}' not found.`));
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Handle dry-run mode
|
|
521
|
+
if (options.dryRun) {
|
|
522
|
+
console.log(chalk.blue(`\n[Dry Run] Analyzing shared layer for updates to '${scopeId}'...`));
|
|
523
|
+
|
|
524
|
+
try {
|
|
525
|
+
const status = await sync.getSyncStatus(scopeId);
|
|
526
|
+
console.log(chalk.dim(`\n Last sync-down: ${status.lastSyncDown || 'Never'}`));
|
|
527
|
+
if (status.pulledCount > 0) {
|
|
528
|
+
console.log(chalk.dim(` Previously pulled: ${status.pulledCount} files`));
|
|
529
|
+
for (const file of status.pulledFiles) {
|
|
530
|
+
console.log(` ${chalk.dim('✓')} ${file}`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
} catch {
|
|
534
|
+
// Ignore errors getting status
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
console.log(chalk.dim('\n Run without --dry-run to execute.\n'));
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
console.log(chalk.blue(`\nPulling shared layer updates into '${scopeId}'...`));
|
|
542
|
+
|
|
543
|
+
try {
|
|
544
|
+
// syncDown signature: syncDown(scopeId, options = {})
|
|
545
|
+
const syncOptions = {
|
|
546
|
+
force: options.resolution === 'keep-shared',
|
|
547
|
+
resolution: options.resolution || 'keep-local',
|
|
548
|
+
};
|
|
549
|
+
const result = await sync.syncDown(scopeId, syncOptions);
|
|
550
|
+
|
|
551
|
+
if (result.success) {
|
|
552
|
+
console.log(chalk.green('\n✓ Sync-down complete!\n'));
|
|
553
|
+
} else {
|
|
554
|
+
console.log(chalk.yellow('\n⚠ Sync-down completed with issues.\n'));
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Handle pulled files - result.pulled is array of { file, scope, target }
|
|
558
|
+
if (result.pulled && result.pulled.length > 0) {
|
|
559
|
+
console.log(chalk.dim(' Pulled files:'));
|
|
560
|
+
for (const item of result.pulled) {
|
|
561
|
+
const displayFile = typeof item === 'string' ? item : `${item.scope}/${item.file}`;
|
|
562
|
+
console.log(` ${chalk.cyan('←')} ${displayFile}`);
|
|
563
|
+
}
|
|
564
|
+
} else {
|
|
565
|
+
console.log(chalk.dim(' No new files to pull.'));
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Handle up-to-date files - result.upToDate is array of { file, scope }
|
|
569
|
+
if (result.upToDate && result.upToDate.length > 0) {
|
|
570
|
+
console.log(chalk.dim('\n Already up-to-date:'));
|
|
571
|
+
for (const item of result.upToDate) {
|
|
572
|
+
const displayFile = typeof item === 'string' ? item : `${item.scope}/${item.file}`;
|
|
573
|
+
console.log(` ${chalk.green('✓')} ${displayFile}`);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Handle conflicts - result.conflicts is array of { file, scope, local, shared, resolution }
|
|
578
|
+
if (result.conflicts && result.conflicts.length > 0) {
|
|
579
|
+
console.log(chalk.yellow('\n Conflicts detected:'));
|
|
580
|
+
for (const conflict of result.conflicts) {
|
|
581
|
+
const file = typeof conflict === 'string' ? conflict : `${conflict.scope}/${conflict.file}`;
|
|
582
|
+
console.log(` ${chalk.yellow('!')} ${file}`);
|
|
583
|
+
|
|
584
|
+
// Attempt to resolve conflict if resolution strategy provided
|
|
585
|
+
if (options.resolution && options.resolution !== 'prompt') {
|
|
586
|
+
const resolveResult = await sync.resolveConflict(conflict, options.resolution);
|
|
587
|
+
if (resolveResult.success) {
|
|
588
|
+
console.log(` ${chalk.green('✓')} Resolved: ${resolveResult.action}`);
|
|
589
|
+
} else {
|
|
590
|
+
console.log(` ${chalk.red('✗')} Failed: ${resolveResult.error}`);
|
|
591
|
+
}
|
|
592
|
+
} else {
|
|
593
|
+
console.log(` ${chalk.dim('Use --resolution to auto-resolve')}`);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Handle errors - result.errors is array of { file, error } or { error }
|
|
599
|
+
if (result.errors && result.errors.length > 0) {
|
|
600
|
+
console.log(chalk.red('\n Errors:'));
|
|
601
|
+
for (const err of result.errors) {
|
|
602
|
+
if (err.file) {
|
|
603
|
+
console.log(` ${chalk.red('✗')} ${err.file}: ${err.error}`);
|
|
604
|
+
} else {
|
|
605
|
+
console.log(` ${chalk.red('✗')} ${err.error}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
console.log();
|
|
611
|
+
} catch (error) {
|
|
612
|
+
console.error(chalk.red(`\nSync-down failed: ${error.message}`));
|
|
613
|
+
if (process.env.DEBUG) {
|
|
614
|
+
console.error(chalk.dim(error.stack));
|
|
615
|
+
}
|
|
616
|
+
process.exit(1);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Handle 'set' subcommand - Set the active scope for the session
|
|
622
|
+
*/
|
|
623
|
+
async function handleSet(projectRoot, scopeId, options) {
|
|
624
|
+
const manager = new ScopeManager({ projectRoot });
|
|
625
|
+
const scopeFilePath = path.join(projectRoot, '.bmad-scope');
|
|
626
|
+
|
|
627
|
+
// If no scopeId provided, show current scope or prompt
|
|
628
|
+
if (!scopeId) {
|
|
629
|
+
// Check if there's a current scope
|
|
630
|
+
try {
|
|
631
|
+
if (await fs.pathExists(scopeFilePath)) {
|
|
632
|
+
const content = await fs.readFile(scopeFilePath, 'utf8');
|
|
633
|
+
const match = content.match(/active_scope:\s*(\S+)/);
|
|
634
|
+
if (match) {
|
|
635
|
+
console.log(chalk.blue(`\nCurrent active scope: ${chalk.cyan(match[1])}\n`));
|
|
636
|
+
|
|
637
|
+
// Offer to change
|
|
638
|
+
const scopes = await manager.listScopes({ status: 'active' });
|
|
639
|
+
if (scopes.length === 0) {
|
|
640
|
+
console.log(chalk.yellow('No active scopes available. Create one with: npx bmad-fh@multi-artifact scope create <id>\n'));
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const choices = scopes.map((s) => ({ value: s.id, label: `${s.id} - ${s.name || 'No name'}` }));
|
|
645
|
+
choices.push({ value: '__clear__', label: 'Clear active scope' });
|
|
646
|
+
|
|
647
|
+
const selected = await select({
|
|
648
|
+
message: 'Select scope to activate:',
|
|
649
|
+
options: choices,
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
if (isCancel(selected)) {
|
|
653
|
+
console.log(chalk.yellow('Cancelled.'));
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (selected === '__clear__') {
|
|
658
|
+
await fs.remove(scopeFilePath);
|
|
659
|
+
console.log(chalk.green('\n✓ Active scope cleared.\n'));
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
scopeId = selected;
|
|
664
|
+
}
|
|
665
|
+
} else {
|
|
666
|
+
// No current scope, prompt to select
|
|
667
|
+
await manager.initialize();
|
|
668
|
+
const scopes = await manager.listScopes({ status: 'active' });
|
|
669
|
+
|
|
670
|
+
if (scopes.length === 0) {
|
|
671
|
+
console.log(chalk.yellow('\nNo scopes available. Create one first:\n'));
|
|
672
|
+
console.log(` ${chalk.cyan('npx bmad-fh@multi-artifact scope create <id>')}\n`);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const choices = scopes.map((s) => ({ value: s.id, label: `${s.id} - ${s.name || 'No name'}` }));
|
|
677
|
+
|
|
678
|
+
const selected = await select({
|
|
679
|
+
message: 'Select scope to activate:',
|
|
680
|
+
options: choices,
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
if (isCancel(selected)) {
|
|
684
|
+
console.log(chalk.yellow('Cancelled.'));
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
scopeId = selected;
|
|
689
|
+
}
|
|
690
|
+
} catch (error) {
|
|
691
|
+
if (error.message.includes('does not exist')) {
|
|
692
|
+
console.log(chalk.yellow('\nScope system not initialized. Run: npx bmad-fh@multi-artifact scope init\n'));
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
throw error;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
// Validate scope exists
|
|
700
|
+
try {
|
|
701
|
+
await manager.initialize();
|
|
702
|
+
const scope = await manager.getScope(scopeId);
|
|
703
|
+
|
|
704
|
+
if (!scope) {
|
|
705
|
+
console.error(chalk.red(`\nError: Scope '${scopeId}' not found.`));
|
|
706
|
+
console.log(chalk.dim('Available scopes:'));
|
|
707
|
+
const scopes = await manager.listScopes({ status: 'active' });
|
|
708
|
+
for (const s of scopes) {
|
|
709
|
+
console.log(` ${chalk.cyan(s.id)} - ${s.name || 'No name'}`);
|
|
710
|
+
}
|
|
711
|
+
console.log();
|
|
712
|
+
process.exit(1);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (scope.status === 'archived') {
|
|
716
|
+
console.error(chalk.yellow(`\nWarning: Scope '${scopeId}' is archived. Activate it first with:`));
|
|
717
|
+
console.log(` ${chalk.cyan(`npx bmad-fh@multi-artifact scope activate ${scopeId}`)}\n`);
|
|
718
|
+
|
|
719
|
+
const proceed = await confirm({
|
|
720
|
+
message: 'Set as active scope anyway?',
|
|
721
|
+
initialValue: false,
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
if (isCancel(proceed) || !proceed) {
|
|
725
|
+
console.log(chalk.yellow('Cancelled.'));
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
} catch (error) {
|
|
730
|
+
if (error.message.includes('does not exist')) {
|
|
731
|
+
console.log(chalk.yellow('\nScope system not initialized. Run: npx bmad-fh@multi-artifact scope init\n'));
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
throw error;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Write .bmad-scope file
|
|
738
|
+
const scopeContent = `# BMAD Active Scope Configuration
|
|
739
|
+
# This file is auto-generated. Do not edit manually.
|
|
740
|
+
# To change: npx bmad-fh@multi-artifact scope set <scope-id>
|
|
741
|
+
|
|
742
|
+
active_scope: ${scopeId}
|
|
743
|
+
set_at: "${new Date().toISOString()}"
|
|
744
|
+
`;
|
|
745
|
+
|
|
746
|
+
await fs.writeFile(scopeFilePath, scopeContent, 'utf8');
|
|
747
|
+
|
|
748
|
+
console.log(chalk.green(`\n✓ Active scope set to '${scopeId}'`));
|
|
749
|
+
console.log(chalk.dim(` File: ${scopeFilePath}`));
|
|
750
|
+
console.log(chalk.dim('\n Workflows will now use this scope automatically.'));
|
|
751
|
+
console.log(chalk.dim(' You can also use BMAD_SCOPE environment variable to override.\n'));
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* Handle 'unset' subcommand - Clear the active scope
|
|
756
|
+
*/
|
|
757
|
+
async function handleUnset(projectRoot) {
|
|
758
|
+
const scopeFilePath = path.join(projectRoot, '.bmad-scope');
|
|
759
|
+
|
|
760
|
+
if (await fs.pathExists(scopeFilePath)) {
|
|
761
|
+
await fs.remove(scopeFilePath);
|
|
762
|
+
console.log(chalk.green('\n✓ Active scope cleared.\n'));
|
|
763
|
+
console.log(chalk.dim(' Workflows will now prompt for scope selection.\n'));
|
|
764
|
+
} else {
|
|
765
|
+
console.log(chalk.yellow('\n No active scope is set.\n'));
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Show comprehensive help for scope command
|
|
361
771
|
*/
|
|
362
772
|
function showHelp() {
|
|
363
|
-
console.log(chalk.bold('\n BMAD Scope Management
|
|
364
|
-
console.log('
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
console.log(
|
|
368
|
-
console.log(
|
|
369
|
-
console.log(
|
|
370
|
-
console.log(
|
|
371
|
-
|
|
372
|
-
console.log(
|
|
773
|
+
console.log(chalk.bold('\n BMAD Scope Management'));
|
|
774
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
775
|
+
|
|
776
|
+
// Overview
|
|
777
|
+
console.log(chalk.bold(' OVERVIEW\n'));
|
|
778
|
+
console.log(' The scope system enables parallel development by isolating artifacts into');
|
|
779
|
+
console.log(' separate workspaces. Each scope maintains its own planning artifacts,');
|
|
780
|
+
console.log(' implementation artifacts, tests, and optionally a scope-specific context.\n');
|
|
781
|
+
|
|
782
|
+
console.log(chalk.dim(' Key Benefits:'));
|
|
783
|
+
console.log(' • Run multiple workflows in parallel across different terminal sessions');
|
|
784
|
+
console.log(' • Isolated artifacts prevent cross-contamination between features/services');
|
|
785
|
+
console.log(' • Shared knowledge layer for cross-cutting concerns and contracts');
|
|
786
|
+
console.log(' • Event system notifies dependent scopes of changes');
|
|
787
|
+
console.log(' • Session-sticky scope context for seamless workflow integration\n');
|
|
788
|
+
|
|
789
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
790
|
+
|
|
791
|
+
// Commands
|
|
792
|
+
console.log(chalk.bold(' COMMANDS\n'));
|
|
793
|
+
console.log(` ${chalk.cyan('init')} Initialize scope system in current project`);
|
|
794
|
+
console.log(` ${chalk.cyan('list')} ${chalk.dim('[options]')} List all scopes (aliases: ls)`);
|
|
795
|
+
console.log(` ${chalk.cyan('create')} ${chalk.dim('[id] [opts]')} Create a new scope (aliases: new)`);
|
|
796
|
+
console.log(` ${chalk.cyan('info')} ${chalk.dim('<id>')} Show detailed scope information (aliases: show)`);
|
|
797
|
+
console.log(` ${chalk.cyan('remove')} ${chalk.dim('<id> [opts]')} Remove a scope and its artifacts (aliases: rm, delete)`);
|
|
798
|
+
console.log(` ${chalk.cyan('archive')} ${chalk.dim('<id>')} Archive a scope (preserves artifacts, excludes from list)`);
|
|
799
|
+
console.log(` ${chalk.cyan('activate')} ${chalk.dim('<id>')} Reactivate an archived scope`);
|
|
800
|
+
console.log(` ${chalk.cyan('set')} ${chalk.dim('[id]')} Set active scope for session (alias: use)`);
|
|
801
|
+
console.log(` ${chalk.cyan('unset')} Clear active scope (alias: clear)`);
|
|
802
|
+
console.log(` ${chalk.cyan('sync-up')} ${chalk.dim('<id> [opts]')} Promote scope artifacts to shared layer (alias: syncup)`);
|
|
803
|
+
console.log(` ${chalk.cyan('sync-down')} ${chalk.dim('<id> [opts]')} Pull shared layer updates into scope (alias: syncdown)`);
|
|
804
|
+
console.log(` ${chalk.cyan('help')} ${chalk.dim('[command]')} Show help (add command name for detailed help)`);
|
|
373
805
|
console.log();
|
|
374
|
-
|
|
375
|
-
console.log(
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
console.log(
|
|
379
|
-
console.log(
|
|
806
|
+
|
|
807
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
808
|
+
|
|
809
|
+
// Options
|
|
810
|
+
console.log(chalk.bold(' OPTIONS\n'));
|
|
811
|
+
console.log(chalk.dim(' Create options:'));
|
|
812
|
+
console.log(` ${chalk.cyan('-n, --name')} ${chalk.dim('<name>')} Human-readable scope name`);
|
|
813
|
+
console.log(` ${chalk.cyan('-d, --description')} ${chalk.dim('<text>')} Brief description of scope purpose`);
|
|
814
|
+
console.log(` ${chalk.cyan('--deps')} ${chalk.dim('<ids>')} Comma-separated dependency scope IDs`);
|
|
815
|
+
console.log(` ${chalk.cyan('--context')} Create scope-specific project-context.md\n`);
|
|
816
|
+
|
|
817
|
+
console.log(chalk.dim(' Remove options:'));
|
|
818
|
+
console.log(` ${chalk.cyan('-f, --force')} Skip confirmation prompt`);
|
|
819
|
+
console.log(` ${chalk.cyan('--no-backup')} Don't create backup before removal\n`);
|
|
820
|
+
|
|
821
|
+
console.log(chalk.dim(' List options:'));
|
|
822
|
+
console.log(` ${chalk.cyan('-s, --status')} ${chalk.dim('<status>')} Filter by status (active|archived)\n`);
|
|
823
|
+
|
|
824
|
+
console.log(chalk.dim(' Sync options:'));
|
|
825
|
+
console.log(` ${chalk.cyan('--dry-run')} Show what would be synced without changes`);
|
|
826
|
+
console.log(
|
|
827
|
+
` ${chalk.cyan('--resolution')} ${chalk.dim('<strategy>')} Conflict resolution: keep-local|keep-shared|backup-and-update\n`,
|
|
828
|
+
);
|
|
829
|
+
|
|
830
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
831
|
+
|
|
832
|
+
// Quick Start
|
|
833
|
+
console.log(chalk.bold(' QUICK START\n'));
|
|
834
|
+
console.log(chalk.dim(' 1. Initialize the scope system:'));
|
|
835
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope init\n`);
|
|
836
|
+
console.log(chalk.dim(' 2. Create your first scope:'));
|
|
837
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope create auth --name "Authentication Service"\n`);
|
|
838
|
+
console.log(chalk.dim(' 3. Set the active scope for your session:'));
|
|
839
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope set auth\n`);
|
|
840
|
+
console.log(chalk.dim(' 4. Run workflows - artifacts go to scope directory:'));
|
|
841
|
+
console.log(` Workflows automatically detect scope from .bmad-scope`);
|
|
842
|
+
console.log(` Your PRD, architecture, etc. are isolated in _bmad-output/auth/\n`);
|
|
843
|
+
console.log(chalk.dim(' Alternative: Use BMAD_SCOPE environment variable to override:\n'));
|
|
844
|
+
console.log(` ${chalk.green('$')} BMAD_SCOPE=auth npx bmad-fh@multi-artifact ...\n`);
|
|
845
|
+
|
|
846
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
847
|
+
|
|
848
|
+
// Examples
|
|
849
|
+
console.log(chalk.bold(' EXAMPLES\n'));
|
|
850
|
+
console.log(chalk.dim(' Basic workflow:'));
|
|
851
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope init`);
|
|
852
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope create auth --name "Auth" --description "User authentication"`);
|
|
853
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope create payments --name "Payments" --deps auth`);
|
|
854
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope list\n`);
|
|
855
|
+
|
|
856
|
+
console.log(chalk.dim(' Parallel development (two terminals):'));
|
|
857
|
+
console.log(` ${chalk.green('# Terminal 1:')} ${chalk.green('# Terminal 2:')}`);
|
|
858
|
+
console.log(
|
|
859
|
+
` ${chalk.dim('$')} npx bmad-fh@multi-artifact scope create auth ${chalk.dim('$')} npx bmad-fh@multi-artifact scope create payments`,
|
|
860
|
+
);
|
|
861
|
+
console.log(` ${chalk.dim('# Run PRD workflow for auth')} ${chalk.dim('# Run PRD workflow for payments')}\n`);
|
|
862
|
+
|
|
863
|
+
console.log(chalk.dim(' Sharing artifacts between scopes:'));
|
|
864
|
+
console.log(
|
|
865
|
+
` ${chalk.green('$')} npx bmad-fh@multi-artifact scope sync-up auth ${chalk.dim('# Promote auth artifacts to _shared/')}`,
|
|
866
|
+
);
|
|
867
|
+
console.log(
|
|
868
|
+
` ${chalk.green('$')} npx bmad-fh@multi-artifact scope sync-down payments ${chalk.dim('# Pull shared updates into payments')}\n`,
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
console.log(chalk.dim(' Lifecycle management:'));
|
|
872
|
+
console.log(
|
|
873
|
+
` ${chalk.green('$')} npx bmad-fh@multi-artifact scope archive auth ${chalk.dim('# Archive when feature is complete')}`,
|
|
874
|
+
);
|
|
875
|
+
console.log(
|
|
876
|
+
` ${chalk.green('$')} npx bmad-fh@multi-artifact scope activate auth ${chalk.dim('# Reactivate if needed later')}`,
|
|
877
|
+
);
|
|
878
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope remove auth ${chalk.dim('# Remove with backup')}`);
|
|
879
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope remove auth --force --no-backup ${chalk.dim('# Force remove')}\n`);
|
|
880
|
+
|
|
881
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
882
|
+
|
|
883
|
+
// Directory Structure
|
|
884
|
+
console.log(chalk.bold(' DIRECTORY STRUCTURE\n'));
|
|
885
|
+
console.log(chalk.dim(' After initialization and scope creation:'));
|
|
380
886
|
console.log();
|
|
381
|
-
console.log(
|
|
382
|
-
console.log(
|
|
383
|
-
console.log(
|
|
384
|
-
console.log(` ${chalk.
|
|
385
|
-
console.log(
|
|
386
|
-
console.log(` ${chalk.
|
|
887
|
+
console.log(' project-root/');
|
|
888
|
+
console.log(' ├── _bmad/');
|
|
889
|
+
console.log(' │ ├── _config/');
|
|
890
|
+
console.log(` │ │ └── ${chalk.cyan('scopes.yaml')} ${chalk.dim('# Scope registry and settings')}`);
|
|
891
|
+
console.log(' │ └── _events/');
|
|
892
|
+
console.log(` │ ├── ${chalk.cyan('event-log.yaml')} ${chalk.dim('# Event history')}`);
|
|
893
|
+
console.log(` │ └── ${chalk.cyan('subscriptions.yaml')} ${chalk.dim('# Cross-scope subscriptions')}`);
|
|
894
|
+
console.log(' │');
|
|
895
|
+
console.log(' ├── _bmad-output/');
|
|
896
|
+
console.log(` │ ├── ${chalk.yellow('_shared/')} ${chalk.dim('# Shared knowledge layer')}`);
|
|
897
|
+
console.log(` │ │ ├── ${chalk.cyan('project-context.md')} ${chalk.dim('# Global project context')}`);
|
|
898
|
+
console.log(` │ │ ├── contracts/ ${chalk.dim('# Integration contracts')}`);
|
|
899
|
+
console.log(` │ │ └── principles/ ${chalk.dim('# Architecture principles')}`);
|
|
900
|
+
console.log(' │ │');
|
|
901
|
+
console.log(` │ ├── ${chalk.green('auth/')} ${chalk.dim('# Auth scope artifacts')}`);
|
|
902
|
+
console.log(' │ │ ├── planning-artifacts/');
|
|
903
|
+
console.log(' │ │ ├── implementation-artifacts/');
|
|
904
|
+
console.log(' │ │ ├── tests/');
|
|
905
|
+
console.log(` │ │ └── ${chalk.cyan('project-context.md')} ${chalk.dim('# Scope-specific context (optional)')}`);
|
|
906
|
+
console.log(' │ │');
|
|
907
|
+
console.log(` │ └── ${chalk.green('payments/')} ${chalk.dim('# Payments scope artifacts')}`);
|
|
908
|
+
console.log(' │ └── ...');
|
|
909
|
+
console.log(' │');
|
|
910
|
+
console.log(` └── ${chalk.cyan('.bmad-scope')} ${chalk.dim('# Session-sticky active scope (gitignored)')}`);
|
|
387
911
|
console.log();
|
|
912
|
+
|
|
913
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
914
|
+
|
|
915
|
+
// Access Model
|
|
916
|
+
console.log(chalk.bold(' ACCESS MODEL\n'));
|
|
917
|
+
console.log(' Scopes follow a "read-any, write-own" isolation model:\n');
|
|
918
|
+
console.log(' ┌─────────────┬─────────────────┬─────────────────┬─────────────┐');
|
|
919
|
+
console.log(' │ Operation │ Own Scope │ Other Scopes │ _shared/ │');
|
|
920
|
+
console.log(' ├─────────────┼─────────────────┼─────────────────┼─────────────┤');
|
|
921
|
+
console.log(
|
|
922
|
+
` │ ${chalk.green('Read')} │ ${chalk.green('✓ Allowed')} │ ${chalk.green('✓ Allowed')} │ ${chalk.green('✓ Allowed')} │`,
|
|
923
|
+
);
|
|
924
|
+
console.log(
|
|
925
|
+
` │ ${chalk.red('Write')} │ ${chalk.green('✓ Allowed')} │ ${chalk.red('✗ Blocked')} │ ${chalk.yellow('via sync-up')} │`,
|
|
926
|
+
);
|
|
927
|
+
console.log(' └─────────────┴─────────────────┴─────────────────┴─────────────┘\n');
|
|
928
|
+
|
|
929
|
+
console.log(chalk.dim(' Isolation modes (configure in scopes.yaml):'));
|
|
930
|
+
console.log(` • ${chalk.cyan('strict')} - Block cross-scope writes (default, recommended)`);
|
|
931
|
+
console.log(` • ${chalk.cyan('warn')} - Allow with warnings`);
|
|
932
|
+
console.log(` • ${chalk.cyan('permissive')} - Allow all (not recommended)\n`);
|
|
933
|
+
|
|
934
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
935
|
+
|
|
936
|
+
// Workflow Integration
|
|
937
|
+
console.log(chalk.bold(' WORKFLOW INTEGRATION\n'));
|
|
938
|
+
console.log(' Workflows automatically detect and use scope context:\n');
|
|
939
|
+
console.log(chalk.dim(' Resolution order:'));
|
|
940
|
+
console.log(' 1. Explicit --scope flag in workflow command');
|
|
941
|
+
console.log(' 2. Session context from .bmad-scope file');
|
|
942
|
+
console.log(' 3. BMAD_SCOPE environment variable');
|
|
943
|
+
console.log(' 4. Prompt user to select or create scope\n');
|
|
944
|
+
|
|
945
|
+
console.log(chalk.dim(' Scope-aware path variables in workflows:'));
|
|
946
|
+
console.log(` • ${chalk.cyan('{scope}')} → Scope ID (e.g., "auth")`);
|
|
947
|
+
console.log(` • ${chalk.cyan('{scope_path}')} → _bmad-output/auth`);
|
|
948
|
+
console.log(` • ${chalk.cyan('{scope_planning}')} → _bmad-output/auth/planning-artifacts`);
|
|
949
|
+
console.log(` • ${chalk.cyan('{scope_implementation}')} → _bmad-output/auth/implementation-artifacts`);
|
|
950
|
+
console.log(` • ${chalk.cyan('{scope_tests}')} → _bmad-output/auth/tests\n`);
|
|
951
|
+
|
|
952
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
953
|
+
|
|
954
|
+
// Use Cases
|
|
955
|
+
console.log(chalk.bold(' USE CASES\n'));
|
|
956
|
+
console.log(chalk.dim(' Multi-team projects:'));
|
|
957
|
+
console.log(' Each team creates their own scope. Shared contracts and architecture');
|
|
958
|
+
console.log(' principles live in _shared/ and are synced as needed.\n');
|
|
959
|
+
|
|
960
|
+
console.log(chalk.dim(' Microservices architecture:'));
|
|
961
|
+
console.log(' One scope per service. Use dependencies to track service relationships.');
|
|
962
|
+
console.log(' Contracts define APIs between services.\n');
|
|
963
|
+
|
|
964
|
+
console.log(chalk.dim(' Parallel feature development:'));
|
|
965
|
+
console.log(' Create scope per major feature. Develop PRD, architecture, and stories');
|
|
966
|
+
console.log(' independently, then merge to main codebase.\n');
|
|
967
|
+
|
|
968
|
+
console.log(chalk.dim(' Experimentation/Spikes:'));
|
|
969
|
+
console.log(' Create a scope for experiments. Archive or remove when done.\n');
|
|
970
|
+
|
|
971
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
972
|
+
|
|
973
|
+
// Troubleshooting
|
|
974
|
+
console.log(chalk.bold(' TROUBLESHOOTING\n'));
|
|
975
|
+
console.log(chalk.dim(' "Scope system not initialized":'));
|
|
976
|
+
console.log(` Run: ${chalk.cyan('npx bmad-fh@multi-artifact scope init')}\n`);
|
|
977
|
+
|
|
978
|
+
console.log(chalk.dim(' "Cannot write to scope X while in scope Y":'));
|
|
979
|
+
console.log(' You are in strict isolation mode. Either:');
|
|
980
|
+
console.log(' • Switch to the correct scope');
|
|
981
|
+
console.log(' • Use sync-up to promote artifacts to _shared/');
|
|
982
|
+
console.log(' • Change isolation_mode in scopes.yaml (not recommended)\n');
|
|
983
|
+
|
|
984
|
+
console.log(chalk.dim(' "No scope set" when running workflow:'));
|
|
985
|
+
console.log(` • Create and use a scope: ${chalk.cyan('npx bmad-fh@multi-artifact scope create myfeature')}`);
|
|
986
|
+
console.log(' • Or run workflow with --scope flag\n');
|
|
987
|
+
|
|
988
|
+
console.log(chalk.dim(' "Circular dependency detected":'));
|
|
989
|
+
console.log(' Scope A depends on B which depends on A. Remove one dependency.\n');
|
|
990
|
+
|
|
991
|
+
console.log(chalk.dim(' Debug mode:'));
|
|
992
|
+
console.log(` Set ${chalk.cyan('DEBUG=true')} environment variable for verbose output.\n`);
|
|
993
|
+
|
|
994
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
|
|
995
|
+
|
|
996
|
+
// More Help
|
|
997
|
+
console.log(chalk.bold(' MORE HELP\n'));
|
|
998
|
+
console.log(` ${chalk.cyan('npx bmad-fh@multi-artifact scope help init')} ${chalk.dim('# Detailed help for init command')}`);
|
|
999
|
+
console.log(` ${chalk.cyan('npx bmad-fh@multi-artifact scope help create')} ${chalk.dim('# Detailed help for create command')}`);
|
|
1000
|
+
console.log(
|
|
1001
|
+
` ${chalk.cyan('npx bmad-fh@multi-artifact scope help sync-up')} ${chalk.dim('# Detailed help for sync operations')}\n`,
|
|
1002
|
+
);
|
|
1003
|
+
|
|
1004
|
+
console.log(chalk.dim(' Documentation:'));
|
|
1005
|
+
console.log(` • Multi-Scope Guide: ${chalk.cyan('docs/multi-scope-guide.md')}`);
|
|
1006
|
+
console.log(` • Full docs: ${chalk.cyan('http://docs.bmad-method.org')}\n`);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Show detailed help for 'init' subcommand
|
|
1011
|
+
*/
|
|
1012
|
+
function showHelpInit() {
|
|
1013
|
+
console.log(chalk.bold('\n bmad scope init'));
|
|
1014
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1015
|
+
|
|
1016
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1017
|
+
console.log(' Initialize the multi-scope system in your project. This command creates the');
|
|
1018
|
+
console.log(' necessary configuration files and directory structure for scope management.\n');
|
|
1019
|
+
|
|
1020
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1021
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope init\n`);
|
|
1022
|
+
|
|
1023
|
+
console.log(chalk.bold(' WHAT IT CREATES\n'));
|
|
1024
|
+
console.log(` ${chalk.cyan('_bmad/_config/scopes.yaml')} Configuration file with scope registry`);
|
|
1025
|
+
console.log(` ${chalk.cyan('_bmad/_events/')} Event system directory`);
|
|
1026
|
+
console.log(` ${chalk.cyan('_bmad-output/_shared/')} Shared knowledge layer\n`);
|
|
1027
|
+
|
|
1028
|
+
console.log(chalk.bold(' NOTES\n'));
|
|
1029
|
+
console.log(' • Safe to run multiple times - will not overwrite existing config');
|
|
1030
|
+
console.log(' • Required before creating any scopes');
|
|
1031
|
+
console.log(' • Automatically run by "scope create" if not initialized\n');
|
|
1032
|
+
|
|
1033
|
+
console.log(chalk.bold(' EXAMPLE\n'));
|
|
1034
|
+
console.log(` ${chalk.green('$')} cd my-project`);
|
|
1035
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope init`);
|
|
1036
|
+
console.log(` ${chalk.dim('✓ Scope system initialized successfully!')}\n`);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Show detailed help for 'create' subcommand
|
|
1041
|
+
*/
|
|
1042
|
+
function showHelpCreate() {
|
|
1043
|
+
console.log(chalk.bold('\n bmad scope create'));
|
|
1044
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1045
|
+
|
|
1046
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1047
|
+
console.log(' Create a new isolated scope for parallel development. Each scope has its own');
|
|
1048
|
+
console.log(' directory structure for artifacts and can optionally declare dependencies on');
|
|
1049
|
+
console.log(' other scopes.\n');
|
|
1050
|
+
|
|
1051
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1052
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope create [id] [options]\n`);
|
|
1053
|
+
|
|
1054
|
+
console.log(chalk.bold(' ARGUMENTS\n'));
|
|
1055
|
+
console.log(` ${chalk.cyan('id')} Scope identifier (lowercase letters, numbers, hyphens)`);
|
|
1056
|
+
console.log(' If omitted, you will be prompted interactively\n');
|
|
1057
|
+
|
|
1058
|
+
console.log(chalk.bold(' OPTIONS\n'));
|
|
1059
|
+
console.log(` ${chalk.cyan('-n, --name')} ${chalk.dim('<name>')}`);
|
|
1060
|
+
console.log(' Human-readable name for the scope');
|
|
1061
|
+
console.log(' Example: --name "Authentication Service"\n');
|
|
1062
|
+
|
|
1063
|
+
console.log(` ${chalk.cyan('-d, --description')} ${chalk.dim('<text>')}`);
|
|
1064
|
+
console.log(' Brief description of the scope purpose');
|
|
1065
|
+
console.log(' Example: --description "Handles user auth, SSO, and sessions"\n');
|
|
1066
|
+
|
|
1067
|
+
console.log(` ${chalk.cyan('--deps, --dependencies')} ${chalk.dim('<ids>')}`);
|
|
1068
|
+
console.log(' Comma-separated list of scope IDs this scope depends on');
|
|
1069
|
+
console.log(' Example: --deps auth,users,notifications\n');
|
|
1070
|
+
|
|
1071
|
+
console.log(` ${chalk.cyan('--context')}`);
|
|
1072
|
+
console.log(' Create a scope-specific project-context.md file');
|
|
1073
|
+
console.log(' Useful when scope needs its own context extending global\n');
|
|
1074
|
+
|
|
1075
|
+
console.log(chalk.bold(' SCOPE ID RULES\n'));
|
|
1076
|
+
console.log(' • Lowercase letters, numbers, and hyphens only');
|
|
1077
|
+
console.log(' • Must start with a letter');
|
|
1078
|
+
console.log(' • Cannot use reserved names: _shared, _backup, _config, _events');
|
|
1079
|
+
console.log(' • Maximum 50 characters\n');
|
|
1080
|
+
|
|
1081
|
+
console.log(chalk.bold(' EXAMPLES\n'));
|
|
1082
|
+
console.log(chalk.dim(' Interactive mode (prompts for all fields):'));
|
|
1083
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope create\n`);
|
|
1084
|
+
|
|
1085
|
+
console.log(chalk.dim(' Quick create with ID only:'));
|
|
1086
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope create auth\n`);
|
|
1087
|
+
|
|
1088
|
+
console.log(chalk.dim(' Full specification:'));
|
|
1089
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope create payments \\`);
|
|
1090
|
+
console.log(` --name "Payment Processing" \\`);
|
|
1091
|
+
console.log(` --description "Stripe integration, invoicing, subscriptions" \\`);
|
|
1092
|
+
console.log(` --deps auth,users \\`);
|
|
1093
|
+
console.log(` --context\n`);
|
|
1094
|
+
|
|
1095
|
+
console.log(chalk.bold(' WHAT IT CREATES\n'));
|
|
1096
|
+
console.log(' _bmad-output/{scope-id}/');
|
|
1097
|
+
console.log(' ├── planning-artifacts/ # PRDs, architecture docs');
|
|
1098
|
+
console.log(' ├── implementation-artifacts/ # Sprint status, stories');
|
|
1099
|
+
console.log(' ├── tests/ # Test artifacts');
|
|
1100
|
+
console.log(' └── project-context.md # If --context specified\n');
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* Show detailed help for 'list' subcommand
|
|
1105
|
+
*/
|
|
1106
|
+
function showHelpList() {
|
|
1107
|
+
console.log(chalk.bold('\n bmad scope list'));
|
|
1108
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1109
|
+
|
|
1110
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1111
|
+
console.log(' List all scopes in the project with their status and metadata.\n');
|
|
1112
|
+
|
|
1113
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1114
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope list [options]`);
|
|
1115
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope ls [options] ${chalk.dim('# alias')}\n`);
|
|
1116
|
+
|
|
1117
|
+
console.log(chalk.bold(' OPTIONS\n'));
|
|
1118
|
+
console.log(` ${chalk.cyan('-s, --status')} ${chalk.dim('<status>')}`);
|
|
1119
|
+
console.log(' Filter by scope status');
|
|
1120
|
+
console.log(' Values: active, archived\n');
|
|
1121
|
+
|
|
1122
|
+
console.log(chalk.bold(' OUTPUT COLUMNS\n'));
|
|
1123
|
+
console.log(` ${chalk.cyan('ID')} Scope identifier`);
|
|
1124
|
+
console.log(` ${chalk.cyan('Name')} Human-readable name`);
|
|
1125
|
+
console.log(` ${chalk.cyan('Status')} active or archived`);
|
|
1126
|
+
console.log(` ${chalk.cyan('Created')} Creation date\n`);
|
|
1127
|
+
|
|
1128
|
+
console.log(chalk.bold(' EXAMPLES\n'));
|
|
1129
|
+
console.log(chalk.dim(' List all scopes:'));
|
|
1130
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope list\n`);
|
|
1131
|
+
|
|
1132
|
+
console.log(chalk.dim(' List only active scopes:'));
|
|
1133
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope list --status active\n`);
|
|
1134
|
+
|
|
1135
|
+
console.log(chalk.dim(' List archived scopes:'));
|
|
1136
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope ls -s archived\n`);
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
/**
|
|
1140
|
+
* Show detailed help for 'info' subcommand
|
|
1141
|
+
*/
|
|
1142
|
+
function showHelpInfo() {
|
|
1143
|
+
console.log(chalk.bold('\n bmad scope info'));
|
|
1144
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1145
|
+
|
|
1146
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1147
|
+
console.log(' Display detailed information about a specific scope including paths,');
|
|
1148
|
+
console.log(' dependencies, dependents, and metadata.\n');
|
|
1149
|
+
|
|
1150
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1151
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope info <id>`);
|
|
1152
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope show <id> ${chalk.dim('# alias')}`);
|
|
1153
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope <id> ${chalk.dim('# shorthand')}\n`);
|
|
1154
|
+
|
|
1155
|
+
console.log(chalk.bold(' ARGUMENTS\n'));
|
|
1156
|
+
console.log(` ${chalk.cyan('id')} Scope identifier (required)\n`);
|
|
1157
|
+
|
|
1158
|
+
console.log(chalk.bold(' DISPLAYED INFORMATION\n'));
|
|
1159
|
+
console.log(' • Basic info: ID, name, description, status');
|
|
1160
|
+
console.log(' • Timestamps: Created, last activity');
|
|
1161
|
+
console.log(' • Metrics: Artifact count');
|
|
1162
|
+
console.log(' • Paths: Planning, implementation, tests directories');
|
|
1163
|
+
console.log(' • Dependencies: Scopes this scope depends on');
|
|
1164
|
+
console.log(' • Dependents: Scopes that depend on this scope\n');
|
|
1165
|
+
|
|
1166
|
+
console.log(chalk.bold(' EXAMPLES\n'));
|
|
1167
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope info auth`);
|
|
1168
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope auth ${chalk.dim('# shorthand')}\n`);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Show detailed help for 'remove' subcommand
|
|
1173
|
+
*/
|
|
1174
|
+
function showHelpRemove() {
|
|
1175
|
+
console.log(chalk.bold('\n bmad scope remove'));
|
|
1176
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1177
|
+
|
|
1178
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1179
|
+
console.log(' Remove a scope and optionally its artifacts. By default, creates a backup');
|
|
1180
|
+
console.log(' before removal and prompts for confirmation.\n');
|
|
1181
|
+
|
|
1182
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1183
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope remove <id> [options]`);
|
|
1184
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope rm <id> ${chalk.dim('# alias')}`);
|
|
1185
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope delete <id> ${chalk.dim('# alias')}\n`);
|
|
1186
|
+
|
|
1187
|
+
console.log(chalk.bold(' ARGUMENTS\n'));
|
|
1188
|
+
console.log(` ${chalk.cyan('id')} Scope identifier to remove (required)\n`);
|
|
1189
|
+
|
|
1190
|
+
console.log(chalk.bold(' OPTIONS\n'));
|
|
1191
|
+
console.log(` ${chalk.cyan('-f, --force')}`);
|
|
1192
|
+
console.log(' Skip confirmation prompt\n');
|
|
1193
|
+
|
|
1194
|
+
console.log(` ${chalk.cyan('--no-backup')}`);
|
|
1195
|
+
console.log(' Do not create backup before removal');
|
|
1196
|
+
console.log(` ${chalk.red('Warning: Artifacts will be permanently deleted!')}\n`);
|
|
1197
|
+
|
|
1198
|
+
console.log(chalk.bold(' BACKUP LOCATION\n'));
|
|
1199
|
+
console.log(' Backups are created at: _bmad-output/_backup_{id}_{timestamp}/\n');
|
|
1200
|
+
|
|
1201
|
+
console.log(chalk.bold(' EXAMPLES\n'));
|
|
1202
|
+
console.log(chalk.dim(' Safe removal (prompts, creates backup):'));
|
|
1203
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope remove auth\n`);
|
|
1204
|
+
|
|
1205
|
+
console.log(chalk.dim(' Force removal without prompt (still creates backup):'));
|
|
1206
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope rm auth --force\n`);
|
|
1207
|
+
|
|
1208
|
+
console.log(chalk.dim(' Permanent removal (no backup, no prompt):'));
|
|
1209
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope delete auth --force --no-backup\n`);
|
|
1210
|
+
|
|
1211
|
+
console.log(chalk.bold(' CONSIDERATIONS\n'));
|
|
1212
|
+
console.log(' • Check dependents first: scopes depending on this will have broken deps');
|
|
1213
|
+
console.log(' • Consider archiving instead if you might need artifacts later');
|
|
1214
|
+
console.log(' • Backup includes all scope artifacts but not _shared/ content\n');
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
/**
|
|
1218
|
+
* Show detailed help for 'archive' subcommand
|
|
1219
|
+
*/
|
|
1220
|
+
function showHelpArchive() {
|
|
1221
|
+
console.log(chalk.bold('\n bmad scope archive'));
|
|
1222
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1223
|
+
|
|
1224
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1225
|
+
console.log(' Archive a scope. Archived scopes are excluded from default listings but');
|
|
1226
|
+
console.log(' retain all artifacts. Use this for completed features or paused work.\n');
|
|
1227
|
+
|
|
1228
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1229
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope archive <id>\n`);
|
|
1230
|
+
|
|
1231
|
+
console.log(chalk.bold(' ARGUMENTS\n'));
|
|
1232
|
+
console.log(` ${chalk.cyan('id')} Scope identifier to archive (required)\n`);
|
|
1233
|
+
|
|
1234
|
+
console.log(chalk.bold(' BEHAVIOR\n'));
|
|
1235
|
+
console.log(' • Scope status changes to "archived"');
|
|
1236
|
+
console.log(' • Artifacts remain intact');
|
|
1237
|
+
console.log(' • Excluded from "scope list" (use --status archived to see)');
|
|
1238
|
+
console.log(' • Can be reactivated with "scope activate"\n');
|
|
1239
|
+
|
|
1240
|
+
console.log(chalk.bold(' EXAMPLES\n'));
|
|
1241
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope archive auth`);
|
|
1242
|
+
console.log(` ${chalk.dim("✓ Scope 'auth' archived.")}\n`);
|
|
1243
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope list --status archived`);
|
|
1244
|
+
console.log(` ${chalk.dim('# Shows auth in archived list')}\n`);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* Show detailed help for 'activate' subcommand
|
|
1249
|
+
*/
|
|
1250
|
+
function showHelpActivate() {
|
|
1251
|
+
console.log(chalk.bold('\n bmad scope activate'));
|
|
1252
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1253
|
+
|
|
1254
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1255
|
+
console.log(' Reactivate an archived scope. The scope will appear in default listings');
|
|
1256
|
+
console.log(' and can be used for workflows again.\n');
|
|
1257
|
+
|
|
1258
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1259
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope activate <id>\n`);
|
|
1260
|
+
|
|
1261
|
+
console.log(chalk.bold(' ARGUMENTS\n'));
|
|
1262
|
+
console.log(` ${chalk.cyan('id')} Scope identifier to activate (required)\n`);
|
|
1263
|
+
|
|
1264
|
+
console.log(chalk.bold(' EXAMPLE\n'));
|
|
1265
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope activate auth`);
|
|
1266
|
+
console.log(` ${chalk.dim("✓ Scope 'auth' activated.")}\n`);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* Show detailed help for 'sync-up' subcommand
|
|
1271
|
+
*/
|
|
1272
|
+
function showHelpSyncUp() {
|
|
1273
|
+
console.log(chalk.bold('\n bmad scope sync-up'));
|
|
1274
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1275
|
+
|
|
1276
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1277
|
+
console.log(' Promote scope artifacts to the shared knowledge layer (_shared/). Use this');
|
|
1278
|
+
console.log(' to share mature artifacts like architecture decisions, contracts, and');
|
|
1279
|
+
console.log(' principles with other scopes.\n');
|
|
1280
|
+
|
|
1281
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1282
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope sync-up <id> [options]\n`);
|
|
1283
|
+
|
|
1284
|
+
console.log(chalk.bold(' ARGUMENTS\n'));
|
|
1285
|
+
console.log(` ${chalk.cyan('id')} Scope identifier to sync from (required)\n`);
|
|
1286
|
+
|
|
1287
|
+
console.log(chalk.bold(' WHAT GETS PROMOTED\n'));
|
|
1288
|
+
console.log(' • architecture/*.md → _shared/architecture/');
|
|
1289
|
+
console.log(' • contracts/*.md → _shared/contracts/');
|
|
1290
|
+
console.log(' • principles/*.md → _shared/principles/');
|
|
1291
|
+
console.log(' • project-context.md → Merged into _shared/project-context.md\n');
|
|
1292
|
+
|
|
1293
|
+
console.log(chalk.bold(' OPTIONS\n'));
|
|
1294
|
+
console.log(` ${chalk.cyan('--dry-run')}`);
|
|
1295
|
+
console.log(' Show what would be promoted without making changes\n');
|
|
1296
|
+
|
|
1297
|
+
console.log(` ${chalk.cyan('--resolution')} ${chalk.dim('<strategy>')}`);
|
|
1298
|
+
console.log(' How to handle conflicts:');
|
|
1299
|
+
console.log(' • keep-local - Keep scope version');
|
|
1300
|
+
console.log(' • keep-shared - Keep shared version');
|
|
1301
|
+
console.log(' • backup-and-update - Backup shared, use scope version\n');
|
|
1302
|
+
|
|
1303
|
+
console.log(chalk.bold(' EXAMPLE\n'));
|
|
1304
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope sync-up auth`);
|
|
1305
|
+
console.log(` ${chalk.dim('Promoted 3 files to _shared/')}`);
|
|
1306
|
+
console.log(` ${chalk.dim(' architecture/auth-design.md')}`);
|
|
1307
|
+
console.log(` ${chalk.dim(' contracts/auth-api.md')}`);
|
|
1308
|
+
console.log(` ${chalk.dim(' principles/security.md')}\n`);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
/**
|
|
1312
|
+
* Show detailed help for 'sync-down' subcommand
|
|
1313
|
+
*/
|
|
1314
|
+
function showHelpSyncDown() {
|
|
1315
|
+
console.log(chalk.bold('\n bmad scope sync-down'));
|
|
1316
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1317
|
+
|
|
1318
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1319
|
+
console.log(' Pull updates from the shared knowledge layer into a scope. Use this to get');
|
|
1320
|
+
console.log(' the latest shared architecture, contracts, and context into your scope.\n');
|
|
1321
|
+
|
|
1322
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1323
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope sync-down <id> [options]\n`);
|
|
1324
|
+
|
|
1325
|
+
console.log(chalk.bold(' ARGUMENTS\n'));
|
|
1326
|
+
console.log(` ${chalk.cyan('id')} Scope identifier to sync to (required)\n`);
|
|
1327
|
+
|
|
1328
|
+
console.log(chalk.bold(' OPTIONS\n'));
|
|
1329
|
+
console.log(` ${chalk.cyan('--dry-run')}`);
|
|
1330
|
+
console.log(' Show what would be pulled without making changes\n');
|
|
1331
|
+
|
|
1332
|
+
console.log(` ${chalk.cyan('--resolution')} ${chalk.dim('<strategy>')}`);
|
|
1333
|
+
console.log(' How to handle conflicts:');
|
|
1334
|
+
console.log(' • keep-local - Keep scope version (default)');
|
|
1335
|
+
console.log(' • keep-shared - Overwrite with shared version');
|
|
1336
|
+
console.log(' • backup-and-update - Backup scope, use shared version\n');
|
|
1337
|
+
|
|
1338
|
+
console.log(chalk.bold(' EXAMPLE\n'));
|
|
1339
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope sync-down payments`);
|
|
1340
|
+
console.log(` ${chalk.dim('Pulled 2 updates from _shared/')}`);
|
|
1341
|
+
console.log(` ${chalk.dim(' contracts/auth-api.md (new)')}`);
|
|
1342
|
+
console.log(` ${chalk.dim(' project-context.md (merged)')}\n`);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/**
|
|
1346
|
+
* Show detailed help for 'set' subcommand
|
|
1347
|
+
*/
|
|
1348
|
+
function showHelpSet() {
|
|
1349
|
+
console.log(chalk.bold('\n bmad scope set'));
|
|
1350
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1351
|
+
|
|
1352
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1353
|
+
console.log(' Set the active scope for your session. This creates a .bmad-scope file in');
|
|
1354
|
+
console.log(' your project root that workflows automatically detect and use.\n');
|
|
1355
|
+
|
|
1356
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1357
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope set [id]`);
|
|
1358
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope use [id] ${chalk.dim('# alias')}\n`);
|
|
1359
|
+
|
|
1360
|
+
console.log(chalk.bold(' ARGUMENTS\n'));
|
|
1361
|
+
console.log(` ${chalk.cyan('id')} Scope identifier to set as active (optional)`);
|
|
1362
|
+
console.log(' If omitted, shows current scope and prompts to select\n');
|
|
1363
|
+
|
|
1364
|
+
console.log(chalk.bold(' BEHAVIOR\n'));
|
|
1365
|
+
console.log(' • Creates/updates .bmad-scope file in project root');
|
|
1366
|
+
console.log(' • .bmad-scope should be added to .gitignore (session-specific)');
|
|
1367
|
+
console.log(' • Workflows automatically detect scope from this file');
|
|
1368
|
+
console.log(' • BMAD_SCOPE environment variable can override\n');
|
|
1369
|
+
|
|
1370
|
+
console.log(chalk.bold(' EXAMPLES\n'));
|
|
1371
|
+
console.log(chalk.dim(' Set a specific scope:'));
|
|
1372
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope set auth\n`);
|
|
1373
|
+
|
|
1374
|
+
console.log(chalk.dim(' Interactive selection (shows current and prompts):'));
|
|
1375
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope set\n`);
|
|
1376
|
+
|
|
1377
|
+
console.log(chalk.dim(' Override with environment variable:'));
|
|
1378
|
+
console.log(` ${chalk.green('$')} BMAD_SCOPE=payments npx bmad-fh@multi-artifact ...\n`);
|
|
1379
|
+
|
|
1380
|
+
console.log(chalk.bold(' FILE FORMAT\n'));
|
|
1381
|
+
console.log(' The .bmad-scope file contains:');
|
|
1382
|
+
console.log(chalk.dim(' active_scope: auth'));
|
|
1383
|
+
console.log(chalk.dim(' set_at: "2026-01-22T10:00:00Z"\n'));
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
/**
|
|
1387
|
+
* Show detailed help for 'unset' subcommand
|
|
1388
|
+
*/
|
|
1389
|
+
function showHelpUnset() {
|
|
1390
|
+
console.log(chalk.bold('\n bmad scope unset'));
|
|
1391
|
+
console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
|
|
1392
|
+
|
|
1393
|
+
console.log(chalk.bold(' DESCRIPTION\n'));
|
|
1394
|
+
console.log(' Clear the active scope by removing the .bmad-scope file. After this,');
|
|
1395
|
+
console.log(' workflows will prompt for scope selection.\n');
|
|
1396
|
+
|
|
1397
|
+
console.log(chalk.bold(' USAGE\n'));
|
|
1398
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope unset`);
|
|
1399
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope clear ${chalk.dim('# alias')}\n`);
|
|
1400
|
+
|
|
1401
|
+
console.log(chalk.bold(' BEHAVIOR\n'));
|
|
1402
|
+
console.log(' • Removes .bmad-scope file from project root');
|
|
1403
|
+
console.log(' • Workflows will prompt for scope selection');
|
|
1404
|
+
console.log(' • Does nothing if no scope is currently set\n');
|
|
1405
|
+
|
|
1406
|
+
console.log(chalk.bold(' EXAMPLE\n'));
|
|
1407
|
+
console.log(` ${chalk.green('$')} npx bmad-fh@multi-artifact scope unset`);
|
|
1408
|
+
console.log(` ${chalk.dim('✓ Active scope cleared.')}\n`);
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* Router for subcommand-specific help
|
|
1413
|
+
* @param {string} subcommand - The subcommand to show help for
|
|
1414
|
+
*/
|
|
1415
|
+
function showSubcommandHelp(subcommand) {
|
|
1416
|
+
const helpFunctions = {
|
|
1417
|
+
init: showHelpInit,
|
|
1418
|
+
create: showHelpCreate,
|
|
1419
|
+
new: showHelpCreate,
|
|
1420
|
+
list: showHelpList,
|
|
1421
|
+
ls: showHelpList,
|
|
1422
|
+
info: showHelpInfo,
|
|
1423
|
+
show: showHelpInfo,
|
|
1424
|
+
remove: showHelpRemove,
|
|
1425
|
+
rm: showHelpRemove,
|
|
1426
|
+
delete: showHelpRemove,
|
|
1427
|
+
archive: showHelpArchive,
|
|
1428
|
+
activate: showHelpActivate,
|
|
1429
|
+
set: showHelpSet,
|
|
1430
|
+
use: showHelpSet,
|
|
1431
|
+
unset: showHelpUnset,
|
|
1432
|
+
clear: showHelpUnset,
|
|
1433
|
+
'sync-up': showHelpSyncUp,
|
|
1434
|
+
syncup: showHelpSyncUp,
|
|
1435
|
+
'sync-down': showHelpSyncDown,
|
|
1436
|
+
syncdown: showHelpSyncDown,
|
|
1437
|
+
};
|
|
1438
|
+
|
|
1439
|
+
if (helpFunctions[subcommand]) {
|
|
1440
|
+
helpFunctions[subcommand]();
|
|
1441
|
+
} else {
|
|
1442
|
+
console.log(chalk.red(`\n Unknown command: ${subcommand}\n`));
|
|
1443
|
+
console.log(` Run ${chalk.cyan('npx bmad-fh@multi-artifact scope help')} to see available commands.\n`);
|
|
1444
|
+
}
|
|
388
1445
|
}
|
|
389
1446
|
|
|
390
1447
|
module.exports = {
|
|
@@ -398,6 +1455,8 @@ module.exports = {
|
|
|
398
1455
|
['--no-backup', 'Skip backup on remove'],
|
|
399
1456
|
['--context', 'Create scope-specific project-context.md'],
|
|
400
1457
|
['-s, --status <status>', 'Filter by status (active/archived)'],
|
|
1458
|
+
['--dry-run', 'Show what would be synced without making changes'],
|
|
1459
|
+
['--resolution <strategy>', 'Conflict resolution: keep-local|keep-shared|backup-and-update'],
|
|
401
1460
|
],
|
|
402
1461
|
action: async (subcommand, id, options) => {
|
|
403
1462
|
try {
|
|
@@ -446,7 +1505,40 @@ module.exports = {
|
|
|
446
1505
|
break;
|
|
447
1506
|
}
|
|
448
1507
|
|
|
449
|
-
case '
|
|
1508
|
+
case 'sync-up':
|
|
1509
|
+
case 'syncup': {
|
|
1510
|
+
await handleSyncUp(projectRoot, id, options);
|
|
1511
|
+
break;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
case 'sync-down':
|
|
1515
|
+
case 'syncdown': {
|
|
1516
|
+
await handleSyncDown(projectRoot, id, options);
|
|
1517
|
+
break;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
case 'set':
|
|
1521
|
+
case 'use': {
|
|
1522
|
+
await handleSet(projectRoot, id, options);
|
|
1523
|
+
break;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
case 'unset':
|
|
1527
|
+
case 'clear': {
|
|
1528
|
+
await handleUnset(projectRoot);
|
|
1529
|
+
break;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
case 'help': {
|
|
1533
|
+
// Check if a subcommand was provided for detailed help
|
|
1534
|
+
if (id) {
|
|
1535
|
+
showSubcommandHelp(id);
|
|
1536
|
+
} else {
|
|
1537
|
+
showHelp();
|
|
1538
|
+
}
|
|
1539
|
+
break;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
450
1542
|
case undefined: {
|
|
451
1543
|
showHelp();
|
|
452
1544
|
break;
|