bmad-fh 6.0.0-alpha.23.50b728f9 → 6.0.0-alpha.23.6390fcb0

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.
@@ -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 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 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 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 (!description) {
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: bmad workflow --scope ${scopeId}`));
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 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 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
- await initializer.removeScope(scopeId, { backup: !options.noBackup });
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 (!options.noBackup) {
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 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 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 scope activate <scope-id>'));
348
359
  process.exit(1);
349
360
  }
350
361
 
@@ -357,39 +368,1124 @@ async function handleActivate(projectRoot, scopeId) {
357
368
  }
358
369
 
359
370
  /**
360
- * Show help for scope command
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 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 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 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 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 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 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 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 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\n'));
364
- console.log(' Manage isolated artifact scopes for parallel development.\n');
365
- console.log(chalk.bold(' Commands:'));
366
- console.log(` ${chalk.cyan('init')} Initialize scope system`);
367
- console.log(` ${chalk.cyan('list')} List all scopes`);
368
- console.log(` ${chalk.cyan('create')} ${chalk.dim('[id]')} Create a new scope`);
369
- console.log(` ${chalk.cyan('info')} ${chalk.dim('<id>')} Show scope details`);
370
- console.log(` ${chalk.cyan('remove')} ${chalk.dim('<id>')} Remove a scope`);
371
- console.log(` ${chalk.cyan('archive')} ${chalk.dim('<id>')} Archive a scope`);
372
- console.log(` ${chalk.cyan('activate')} ${chalk.dim('<id>')} Activate an archived scope`);
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
- console.log(chalk.bold(' Options:'));
375
- console.log(` ${chalk.cyan('-n, --name')} ${chalk.dim('<name>')} Scope name for create`);
376
- console.log(` ${chalk.cyan('-d, --description')} ${chalk.dim('<desc>')} Scope description`);
377
- console.log(` ${chalk.cyan('-f, --force')} Force removal without confirmation`);
378
- console.log(` ${chalk.cyan('--no-backup')} Don't create backup on remove`);
379
- console.log(` ${chalk.cyan('--context')} Create scope-specific project-context.md`);
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 scope init\n`);
836
+ console.log(chalk.dim(' 2. Create your first scope:'));
837
+ console.log(` ${chalk.green('$')} npx bmad-fh 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 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 ...\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 scope init`);
852
+ console.log(` ${chalk.green('$')} npx bmad-fh scope create auth --name "Auth" --description "User authentication"`);
853
+ console.log(` ${chalk.green('$')} npx bmad-fh scope create payments --name "Payments" --deps auth`);
854
+ console.log(` ${chalk.green('$')} npx bmad-fh 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(` ${chalk.dim('$')} npx bmad-fh scope create auth ${chalk.dim('$')} npx bmad-fh scope create payments`);
859
+ console.log(` ${chalk.dim('# Run PRD workflow for auth')} ${chalk.dim('# Run PRD workflow for payments')}\n`);
860
+
861
+ console.log(chalk.dim(' Sharing artifacts between scopes:'));
862
+ console.log(` ${chalk.green('$')} npx bmad-fh scope sync-up auth ${chalk.dim('# Promote auth artifacts to _shared/')}`);
863
+ console.log(` ${chalk.green('$')} npx bmad-fh scope sync-down payments ${chalk.dim('# Pull shared updates into payments')}\n`);
864
+
865
+ console.log(chalk.dim(' Lifecycle management:'));
866
+ console.log(` ${chalk.green('$')} npx bmad-fh scope archive auth ${chalk.dim('# Archive when feature is complete')}`);
867
+ console.log(` ${chalk.green('$')} npx bmad-fh scope activate auth ${chalk.dim('# Reactivate if needed later')}`);
868
+ console.log(` ${chalk.green('$')} npx bmad-fh scope remove auth ${chalk.dim('# Remove with backup')}`);
869
+ console.log(` ${chalk.green('$')} npx bmad-fh scope remove auth --force --no-backup ${chalk.dim('# Force remove')}\n`);
870
+
871
+ console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
872
+
873
+ // Directory Structure
874
+ console.log(chalk.bold(' DIRECTORY STRUCTURE\n'));
875
+ console.log(chalk.dim(' After initialization and scope creation:'));
380
876
  console.log();
381
- console.log(chalk.bold(' Examples:'));
382
- console.log(` ${chalk.dim('$')} bmad scope init`);
383
- console.log(` ${chalk.dim('$')} bmad scope create auth --name "Authentication"`);
384
- console.log(` ${chalk.dim('$')} bmad scope list`);
385
- console.log(` ${chalk.dim('$')} bmad scope info auth`);
386
- console.log(` ${chalk.dim('$')} bmad scope remove auth --force`);
877
+ console.log(' project-root/');
878
+ console.log(' ├── _bmad/');
879
+ console.log(' │ ├── _config/');
880
+ console.log(` │ │ └── ${chalk.cyan('scopes.yaml')} ${chalk.dim('# Scope registry and settings')}`);
881
+ console.log(' │ └── _events/');
882
+ console.log(` │ ├── ${chalk.cyan('event-log.yaml')} ${chalk.dim('# Event history')}`);
883
+ console.log(` │ └── ${chalk.cyan('subscriptions.yaml')} ${chalk.dim('# Cross-scope subscriptions')}`);
884
+ console.log(' │');
885
+ console.log(' ├── _bmad-output/');
886
+ console.log(` │ ├── ${chalk.yellow('_shared/')} ${chalk.dim('# Shared knowledge layer')}`);
887
+ console.log(` │ │ ├── ${chalk.cyan('project-context.md')} ${chalk.dim('# Global project context')}`);
888
+ console.log(` │ │ ├── contracts/ ${chalk.dim('# Integration contracts')}`);
889
+ console.log(` │ │ └── principles/ ${chalk.dim('# Architecture principles')}`);
890
+ console.log(' │ │');
891
+ console.log(` │ ├── ${chalk.green('auth/')} ${chalk.dim('# Auth scope artifacts')}`);
892
+ console.log(' │ │ ├── planning-artifacts/');
893
+ console.log(' │ │ ├── implementation-artifacts/');
894
+ console.log(' │ │ ├── tests/');
895
+ console.log(` │ │ └── ${chalk.cyan('project-context.md')} ${chalk.dim('# Scope-specific context (optional)')}`);
896
+ console.log(' │ │');
897
+ console.log(` │ └── ${chalk.green('payments/')} ${chalk.dim('# Payments scope artifacts')}`);
898
+ console.log(' │ └── ...');
899
+ console.log(' │');
900
+ console.log(` └── ${chalk.cyan('.bmad-scope')} ${chalk.dim('# Session-sticky active scope (gitignored)')}`);
387
901
  console.log();
902
+
903
+ console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
904
+
905
+ // Access Model
906
+ console.log(chalk.bold(' ACCESS MODEL\n'));
907
+ console.log(' Scopes follow a "read-any, write-own" isolation model:\n');
908
+ console.log(' ┌─────────────┬─────────────────┬─────────────────┬─────────────┐');
909
+ console.log(' │ Operation │ Own Scope │ Other Scopes │ _shared/ │');
910
+ console.log(' ├─────────────┼─────────────────┼─────────────────┼─────────────┤');
911
+ console.log(
912
+ ` │ ${chalk.green('Read')} │ ${chalk.green('✓ Allowed')} │ ${chalk.green('✓ Allowed')} │ ${chalk.green('✓ Allowed')} │`,
913
+ );
914
+ console.log(
915
+ ` │ ${chalk.red('Write')} │ ${chalk.green('✓ Allowed')} │ ${chalk.red('✗ Blocked')} │ ${chalk.yellow('via sync-up')} │`,
916
+ );
917
+ console.log(' └─────────────┴─────────────────┴─────────────────┴─────────────┘\n');
918
+
919
+ console.log(chalk.dim(' Isolation modes (configure in scopes.yaml):'));
920
+ console.log(` • ${chalk.cyan('strict')} - Block cross-scope writes (default, recommended)`);
921
+ console.log(` • ${chalk.cyan('warn')} - Allow with warnings`);
922
+ console.log(` • ${chalk.cyan('permissive')} - Allow all (not recommended)\n`);
923
+
924
+ console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
925
+
926
+ // Workflow Integration
927
+ console.log(chalk.bold(' WORKFLOW INTEGRATION\n'));
928
+ console.log(' Workflows automatically detect and use scope context:\n');
929
+ console.log(chalk.dim(' Resolution order:'));
930
+ console.log(' 1. Explicit --scope flag in workflow command');
931
+ console.log(' 2. Session context from .bmad-scope file');
932
+ console.log(' 3. BMAD_SCOPE environment variable');
933
+ console.log(' 4. Prompt user to select or create scope\n');
934
+
935
+ console.log(chalk.dim(' Scope-aware path variables in workflows:'));
936
+ console.log(` • ${chalk.cyan('{scope}')} → Scope ID (e.g., "auth")`);
937
+ console.log(` • ${chalk.cyan('{scope_path}')} → _bmad-output/auth`);
938
+ console.log(` • ${chalk.cyan('{scope_planning}')} → _bmad-output/auth/planning-artifacts`);
939
+ console.log(` • ${chalk.cyan('{scope_implementation}')} → _bmad-output/auth/implementation-artifacts`);
940
+ console.log(` • ${chalk.cyan('{scope_tests}')} → _bmad-output/auth/tests\n`);
941
+
942
+ console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
943
+
944
+ // Use Cases
945
+ console.log(chalk.bold(' USE CASES\n'));
946
+ console.log(chalk.dim(' Multi-team projects:'));
947
+ console.log(' Each team creates their own scope. Shared contracts and architecture');
948
+ console.log(' principles live in _shared/ and are synced as needed.\n');
949
+
950
+ console.log(chalk.dim(' Microservices architecture:'));
951
+ console.log(' One scope per service. Use dependencies to track service relationships.');
952
+ console.log(' Contracts define APIs between services.\n');
953
+
954
+ console.log(chalk.dim(' Parallel feature development:'));
955
+ console.log(' Create scope per major feature. Develop PRD, architecture, and stories');
956
+ console.log(' independently, then merge to main codebase.\n');
957
+
958
+ console.log(chalk.dim(' Experimentation/Spikes:'));
959
+ console.log(' Create a scope for experiments. Archive or remove when done.\n');
960
+
961
+ console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
962
+
963
+ // Troubleshooting
964
+ console.log(chalk.bold(' TROUBLESHOOTING\n'));
965
+ console.log(chalk.dim(' "Scope system not initialized":'));
966
+ console.log(` Run: ${chalk.cyan('npx bmad-fh scope init')}\n`);
967
+
968
+ console.log(chalk.dim(' "Cannot write to scope X while in scope Y":'));
969
+ console.log(' You are in strict isolation mode. Either:');
970
+ console.log(' • Switch to the correct scope');
971
+ console.log(' • Use sync-up to promote artifacts to _shared/');
972
+ console.log(' • Change isolation_mode in scopes.yaml (not recommended)\n');
973
+
974
+ console.log(chalk.dim(' "No scope set" when running workflow:'));
975
+ console.log(` • Create and use a scope: ${chalk.cyan('npx bmad-fh scope create myfeature')}`);
976
+ console.log(' • Or run workflow with --scope flag\n');
977
+
978
+ console.log(chalk.dim(' "Circular dependency detected":'));
979
+ console.log(' Scope A depends on B which depends on A. Remove one dependency.\n');
980
+
981
+ console.log(chalk.dim(' Debug mode:'));
982
+ console.log(` Set ${chalk.cyan('DEBUG=true')} environment variable for verbose output.\n`);
983
+
984
+ console.log(chalk.dim(' ─────────────────────────────────────────────────────────────────────────────\n'));
985
+
986
+ // More Help
987
+ console.log(chalk.bold(' MORE HELP\n'));
988
+ console.log(` ${chalk.cyan('npx bmad-fh scope help init')} ${chalk.dim('# Detailed help for init command')}`);
989
+ console.log(` ${chalk.cyan('npx bmad-fh scope help create')} ${chalk.dim('# Detailed help for create command')}`);
990
+ console.log(` ${chalk.cyan('npx bmad-fh scope help sync-up')} ${chalk.dim('# Detailed help for sync operations')}\n`);
991
+
992
+ console.log(chalk.dim(' Documentation:'));
993
+ console.log(` • Multi-Scope Guide: ${chalk.cyan('docs/multi-scope-guide.md')}`);
994
+ console.log(` • Full docs: ${chalk.cyan('http://docs.bmad-method.org')}\n`);
995
+ }
996
+
997
+ /**
998
+ * Show detailed help for 'init' subcommand
999
+ */
1000
+ function showHelpInit() {
1001
+ console.log(chalk.bold('\n bmad scope init'));
1002
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1003
+
1004
+ console.log(chalk.bold(' DESCRIPTION\n'));
1005
+ console.log(' Initialize the multi-scope system in your project. This command creates the');
1006
+ console.log(' necessary configuration files and directory structure for scope management.\n');
1007
+
1008
+ console.log(chalk.bold(' USAGE\n'));
1009
+ console.log(` ${chalk.green('$')} npx bmad-fh scope init\n`);
1010
+
1011
+ console.log(chalk.bold(' WHAT IT CREATES\n'));
1012
+ console.log(` ${chalk.cyan('_bmad/_config/scopes.yaml')} Configuration file with scope registry`);
1013
+ console.log(` ${chalk.cyan('_bmad/_events/')} Event system directory`);
1014
+ console.log(` ${chalk.cyan('_bmad-output/_shared/')} Shared knowledge layer\n`);
1015
+
1016
+ console.log(chalk.bold(' NOTES\n'));
1017
+ console.log(' • Safe to run multiple times - will not overwrite existing config');
1018
+ console.log(' • Required before creating any scopes');
1019
+ console.log(' • Automatically run by "scope create" if not initialized\n');
1020
+
1021
+ console.log(chalk.bold(' EXAMPLE\n'));
1022
+ console.log(` ${chalk.green('$')} cd my-project`);
1023
+ console.log(` ${chalk.green('$')} npx bmad-fh scope init`);
1024
+ console.log(` ${chalk.dim('✓ Scope system initialized successfully!')}\n`);
1025
+ }
1026
+
1027
+ /**
1028
+ * Show detailed help for 'create' subcommand
1029
+ */
1030
+ function showHelpCreate() {
1031
+ console.log(chalk.bold('\n bmad scope create'));
1032
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1033
+
1034
+ console.log(chalk.bold(' DESCRIPTION\n'));
1035
+ console.log(' Create a new isolated scope for parallel development. Each scope has its own');
1036
+ console.log(' directory structure for artifacts and can optionally declare dependencies on');
1037
+ console.log(' other scopes.\n');
1038
+
1039
+ console.log(chalk.bold(' USAGE\n'));
1040
+ console.log(` ${chalk.green('$')} npx bmad-fh scope create [id] [options]\n`);
1041
+
1042
+ console.log(chalk.bold(' ARGUMENTS\n'));
1043
+ console.log(` ${chalk.cyan('id')} Scope identifier (lowercase letters, numbers, hyphens)`);
1044
+ console.log(' If omitted, you will be prompted interactively\n');
1045
+
1046
+ console.log(chalk.bold(' OPTIONS\n'));
1047
+ console.log(` ${chalk.cyan('-n, --name')} ${chalk.dim('<name>')}`);
1048
+ console.log(' Human-readable name for the scope');
1049
+ console.log(' Example: --name "Authentication Service"\n');
1050
+
1051
+ console.log(` ${chalk.cyan('-d, --description')} ${chalk.dim('<text>')}`);
1052
+ console.log(' Brief description of the scope purpose');
1053
+ console.log(' Example: --description "Handles user auth, SSO, and sessions"\n');
1054
+
1055
+ console.log(` ${chalk.cyan('--deps, --dependencies')} ${chalk.dim('<ids>')}`);
1056
+ console.log(' Comma-separated list of scope IDs this scope depends on');
1057
+ console.log(' Example: --deps auth,users,notifications\n');
1058
+
1059
+ console.log(` ${chalk.cyan('--context')}`);
1060
+ console.log(' Create a scope-specific project-context.md file');
1061
+ console.log(' Useful when scope needs its own context extending global\n');
1062
+
1063
+ console.log(chalk.bold(' SCOPE ID RULES\n'));
1064
+ console.log(' • Lowercase letters, numbers, and hyphens only');
1065
+ console.log(' • Must start with a letter');
1066
+ console.log(' • Cannot use reserved names: _shared, _backup, _config, _events');
1067
+ console.log(' • Maximum 50 characters\n');
1068
+
1069
+ console.log(chalk.bold(' EXAMPLES\n'));
1070
+ console.log(chalk.dim(' Interactive mode (prompts for all fields):'));
1071
+ console.log(` ${chalk.green('$')} npx bmad-fh scope create\n`);
1072
+
1073
+ console.log(chalk.dim(' Quick create with ID only:'));
1074
+ console.log(` ${chalk.green('$')} npx bmad-fh scope create auth\n`);
1075
+
1076
+ console.log(chalk.dim(' Full specification:'));
1077
+ console.log(` ${chalk.green('$')} npx bmad-fh scope create payments \\`);
1078
+ console.log(` --name "Payment Processing" \\`);
1079
+ console.log(` --description "Stripe integration, invoicing, subscriptions" \\`);
1080
+ console.log(` --deps auth,users \\`);
1081
+ console.log(` --context\n`);
1082
+
1083
+ console.log(chalk.bold(' WHAT IT CREATES\n'));
1084
+ console.log(' _bmad-output/{scope-id}/');
1085
+ console.log(' ├── planning-artifacts/ # PRDs, architecture docs');
1086
+ console.log(' ├── implementation-artifacts/ # Sprint status, stories');
1087
+ console.log(' ├── tests/ # Test artifacts');
1088
+ console.log(' └── project-context.md # If --context specified\n');
1089
+ }
1090
+
1091
+ /**
1092
+ * Show detailed help for 'list' subcommand
1093
+ */
1094
+ function showHelpList() {
1095
+ console.log(chalk.bold('\n bmad scope list'));
1096
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1097
+
1098
+ console.log(chalk.bold(' DESCRIPTION\n'));
1099
+ console.log(' List all scopes in the project with their status and metadata.\n');
1100
+
1101
+ console.log(chalk.bold(' USAGE\n'));
1102
+ console.log(` ${chalk.green('$')} npx bmad-fh scope list [options]`);
1103
+ console.log(` ${chalk.green('$')} npx bmad-fh scope ls [options] ${chalk.dim('# alias')}\n`);
1104
+
1105
+ console.log(chalk.bold(' OPTIONS\n'));
1106
+ console.log(` ${chalk.cyan('-s, --status')} ${chalk.dim('<status>')}`);
1107
+ console.log(' Filter by scope status');
1108
+ console.log(' Values: active, archived\n');
1109
+
1110
+ console.log(chalk.bold(' OUTPUT COLUMNS\n'));
1111
+ console.log(` ${chalk.cyan('ID')} Scope identifier`);
1112
+ console.log(` ${chalk.cyan('Name')} Human-readable name`);
1113
+ console.log(` ${chalk.cyan('Status')} active or archived`);
1114
+ console.log(` ${chalk.cyan('Created')} Creation date\n`);
1115
+
1116
+ console.log(chalk.bold(' EXAMPLES\n'));
1117
+ console.log(chalk.dim(' List all scopes:'));
1118
+ console.log(` ${chalk.green('$')} npx bmad-fh scope list\n`);
1119
+
1120
+ console.log(chalk.dim(' List only active scopes:'));
1121
+ console.log(` ${chalk.green('$')} npx bmad-fh scope list --status active\n`);
1122
+
1123
+ console.log(chalk.dim(' List archived scopes:'));
1124
+ console.log(` ${chalk.green('$')} npx bmad-fh scope ls -s archived\n`);
1125
+ }
1126
+
1127
+ /**
1128
+ * Show detailed help for 'info' subcommand
1129
+ */
1130
+ function showHelpInfo() {
1131
+ console.log(chalk.bold('\n bmad scope info'));
1132
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1133
+
1134
+ console.log(chalk.bold(' DESCRIPTION\n'));
1135
+ console.log(' Display detailed information about a specific scope including paths,');
1136
+ console.log(' dependencies, dependents, and metadata.\n');
1137
+
1138
+ console.log(chalk.bold(' USAGE\n'));
1139
+ console.log(` ${chalk.green('$')} npx bmad-fh scope info <id>`);
1140
+ console.log(` ${chalk.green('$')} npx bmad-fh scope show <id> ${chalk.dim('# alias')}`);
1141
+ console.log(` ${chalk.green('$')} npx bmad-fh scope <id> ${chalk.dim('# shorthand')}\n`);
1142
+
1143
+ console.log(chalk.bold(' ARGUMENTS\n'));
1144
+ console.log(` ${chalk.cyan('id')} Scope identifier (required)\n`);
1145
+
1146
+ console.log(chalk.bold(' DISPLAYED INFORMATION\n'));
1147
+ console.log(' • Basic info: ID, name, description, status');
1148
+ console.log(' • Timestamps: Created, last activity');
1149
+ console.log(' • Metrics: Artifact count');
1150
+ console.log(' • Paths: Planning, implementation, tests directories');
1151
+ console.log(' • Dependencies: Scopes this scope depends on');
1152
+ console.log(' • Dependents: Scopes that depend on this scope\n');
1153
+
1154
+ console.log(chalk.bold(' EXAMPLES\n'));
1155
+ console.log(` ${chalk.green('$')} npx bmad-fh scope info auth`);
1156
+ console.log(` ${chalk.green('$')} npx bmad-fh scope auth ${chalk.dim('# shorthand')}\n`);
1157
+ }
1158
+
1159
+ /**
1160
+ * Show detailed help for 'remove' subcommand
1161
+ */
1162
+ function showHelpRemove() {
1163
+ console.log(chalk.bold('\n bmad scope remove'));
1164
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1165
+
1166
+ console.log(chalk.bold(' DESCRIPTION\n'));
1167
+ console.log(' Remove a scope and optionally its artifacts. By default, creates a backup');
1168
+ console.log(' before removal and prompts for confirmation.\n');
1169
+
1170
+ console.log(chalk.bold(' USAGE\n'));
1171
+ console.log(` ${chalk.green('$')} npx bmad-fh scope remove <id> [options]`);
1172
+ console.log(` ${chalk.green('$')} npx bmad-fh scope rm <id> ${chalk.dim('# alias')}`);
1173
+ console.log(` ${chalk.green('$')} npx bmad-fh scope delete <id> ${chalk.dim('# alias')}\n`);
1174
+
1175
+ console.log(chalk.bold(' ARGUMENTS\n'));
1176
+ console.log(` ${chalk.cyan('id')} Scope identifier to remove (required)\n`);
1177
+
1178
+ console.log(chalk.bold(' OPTIONS\n'));
1179
+ console.log(` ${chalk.cyan('-f, --force')}`);
1180
+ console.log(' Skip confirmation prompt\n');
1181
+
1182
+ console.log(` ${chalk.cyan('--no-backup')}`);
1183
+ console.log(' Do not create backup before removal');
1184
+ console.log(` ${chalk.red('Warning: Artifacts will be permanently deleted!')}\n`);
1185
+
1186
+ console.log(chalk.bold(' BACKUP LOCATION\n'));
1187
+ console.log(' Backups are created at: _bmad-output/_backup_{id}_{timestamp}/\n');
1188
+
1189
+ console.log(chalk.bold(' EXAMPLES\n'));
1190
+ console.log(chalk.dim(' Safe removal (prompts, creates backup):'));
1191
+ console.log(` ${chalk.green('$')} npx bmad-fh scope remove auth\n`);
1192
+
1193
+ console.log(chalk.dim(' Force removal without prompt (still creates backup):'));
1194
+ console.log(` ${chalk.green('$')} npx bmad-fh scope rm auth --force\n`);
1195
+
1196
+ console.log(chalk.dim(' Permanent removal (no backup, no prompt):'));
1197
+ console.log(` ${chalk.green('$')} npx bmad-fh scope delete auth --force --no-backup\n`);
1198
+
1199
+ console.log(chalk.bold(' CONSIDERATIONS\n'));
1200
+ console.log(' • Check dependents first: scopes depending on this will have broken deps');
1201
+ console.log(' • Consider archiving instead if you might need artifacts later');
1202
+ console.log(' • Backup includes all scope artifacts but not _shared/ content\n');
1203
+ }
1204
+
1205
+ /**
1206
+ * Show detailed help for 'archive' subcommand
1207
+ */
1208
+ function showHelpArchive() {
1209
+ console.log(chalk.bold('\n bmad scope archive'));
1210
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1211
+
1212
+ console.log(chalk.bold(' DESCRIPTION\n'));
1213
+ console.log(' Archive a scope. Archived scopes are excluded from default listings but');
1214
+ console.log(' retain all artifacts. Use this for completed features or paused work.\n');
1215
+
1216
+ console.log(chalk.bold(' USAGE\n'));
1217
+ console.log(` ${chalk.green('$')} npx bmad-fh scope archive <id>\n`);
1218
+
1219
+ console.log(chalk.bold(' ARGUMENTS\n'));
1220
+ console.log(` ${chalk.cyan('id')} Scope identifier to archive (required)\n`);
1221
+
1222
+ console.log(chalk.bold(' BEHAVIOR\n'));
1223
+ console.log(' • Scope status changes to "archived"');
1224
+ console.log(' • Artifacts remain intact');
1225
+ console.log(' • Excluded from "scope list" (use --status archived to see)');
1226
+ console.log(' • Can be reactivated with "scope activate"\n');
1227
+
1228
+ console.log(chalk.bold(' EXAMPLES\n'));
1229
+ console.log(` ${chalk.green('$')} npx bmad-fh scope archive auth`);
1230
+ console.log(` ${chalk.dim("✓ Scope 'auth' archived.")}\n`);
1231
+ console.log(` ${chalk.green('$')} npx bmad-fh scope list --status archived`);
1232
+ console.log(` ${chalk.dim('# Shows auth in archived list')}\n`);
1233
+ }
1234
+
1235
+ /**
1236
+ * Show detailed help for 'activate' subcommand
1237
+ */
1238
+ function showHelpActivate() {
1239
+ console.log(chalk.bold('\n bmad scope activate'));
1240
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1241
+
1242
+ console.log(chalk.bold(' DESCRIPTION\n'));
1243
+ console.log(' Reactivate an archived scope. The scope will appear in default listings');
1244
+ console.log(' and can be used for workflows again.\n');
1245
+
1246
+ console.log(chalk.bold(' USAGE\n'));
1247
+ console.log(` ${chalk.green('$')} npx bmad-fh scope activate <id>\n`);
1248
+
1249
+ console.log(chalk.bold(' ARGUMENTS\n'));
1250
+ console.log(` ${chalk.cyan('id')} Scope identifier to activate (required)\n`);
1251
+
1252
+ console.log(chalk.bold(' EXAMPLE\n'));
1253
+ console.log(` ${chalk.green('$')} npx bmad-fh scope activate auth`);
1254
+ console.log(` ${chalk.dim("✓ Scope 'auth' activated.")}\n`);
1255
+ }
1256
+
1257
+ /**
1258
+ * Show detailed help for 'sync-up' subcommand
1259
+ */
1260
+ function showHelpSyncUp() {
1261
+ console.log(chalk.bold('\n bmad scope sync-up'));
1262
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1263
+
1264
+ console.log(chalk.bold(' DESCRIPTION\n'));
1265
+ console.log(' Promote scope artifacts to the shared knowledge layer (_shared/). Use this');
1266
+ console.log(' to share mature artifacts like architecture decisions, contracts, and');
1267
+ console.log(' principles with other scopes.\n');
1268
+
1269
+ console.log(chalk.bold(' USAGE\n'));
1270
+ console.log(` ${chalk.green('$')} npx bmad-fh scope sync-up <id> [options]\n`);
1271
+
1272
+ console.log(chalk.bold(' ARGUMENTS\n'));
1273
+ console.log(` ${chalk.cyan('id')} Scope identifier to sync from (required)\n`);
1274
+
1275
+ console.log(chalk.bold(' WHAT GETS PROMOTED\n'));
1276
+ console.log(' • architecture/*.md → _shared/architecture/');
1277
+ console.log(' • contracts/*.md → _shared/contracts/');
1278
+ console.log(' • principles/*.md → _shared/principles/');
1279
+ console.log(' • project-context.md → Merged into _shared/project-context.md\n');
1280
+
1281
+ console.log(chalk.bold(' OPTIONS\n'));
1282
+ console.log(` ${chalk.cyan('--dry-run')}`);
1283
+ console.log(' Show what would be promoted without making changes\n');
1284
+
1285
+ console.log(` ${chalk.cyan('--resolution')} ${chalk.dim('<strategy>')}`);
1286
+ console.log(' How to handle conflicts:');
1287
+ console.log(' • keep-local - Keep scope version');
1288
+ console.log(' • keep-shared - Keep shared version');
1289
+ console.log(' • backup-and-update - Backup shared, use scope version\n');
1290
+
1291
+ console.log(chalk.bold(' EXAMPLE\n'));
1292
+ console.log(` ${chalk.green('$')} npx bmad-fh scope sync-up auth`);
1293
+ console.log(` ${chalk.dim('Promoted 3 files to _shared/')}`);
1294
+ console.log(` ${chalk.dim(' architecture/auth-design.md')}`);
1295
+ console.log(` ${chalk.dim(' contracts/auth-api.md')}`);
1296
+ console.log(` ${chalk.dim(' principles/security.md')}\n`);
1297
+ }
1298
+
1299
+ /**
1300
+ * Show detailed help for 'sync-down' subcommand
1301
+ */
1302
+ function showHelpSyncDown() {
1303
+ console.log(chalk.bold('\n bmad scope sync-down'));
1304
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1305
+
1306
+ console.log(chalk.bold(' DESCRIPTION\n'));
1307
+ console.log(' Pull updates from the shared knowledge layer into a scope. Use this to get');
1308
+ console.log(' the latest shared architecture, contracts, and context into your scope.\n');
1309
+
1310
+ console.log(chalk.bold(' USAGE\n'));
1311
+ console.log(` ${chalk.green('$')} npx bmad-fh scope sync-down <id> [options]\n`);
1312
+
1313
+ console.log(chalk.bold(' ARGUMENTS\n'));
1314
+ console.log(` ${chalk.cyan('id')} Scope identifier to sync to (required)\n`);
1315
+
1316
+ console.log(chalk.bold(' OPTIONS\n'));
1317
+ console.log(` ${chalk.cyan('--dry-run')}`);
1318
+ console.log(' Show what would be pulled without making changes\n');
1319
+
1320
+ console.log(` ${chalk.cyan('--resolution')} ${chalk.dim('<strategy>')}`);
1321
+ console.log(' How to handle conflicts:');
1322
+ console.log(' • keep-local - Keep scope version (default)');
1323
+ console.log(' • keep-shared - Overwrite with shared version');
1324
+ console.log(' • backup-and-update - Backup scope, use shared version\n');
1325
+
1326
+ console.log(chalk.bold(' EXAMPLE\n'));
1327
+ console.log(` ${chalk.green('$')} npx bmad-fh scope sync-down payments`);
1328
+ console.log(` ${chalk.dim('Pulled 2 updates from _shared/')}`);
1329
+ console.log(` ${chalk.dim(' contracts/auth-api.md (new)')}`);
1330
+ console.log(` ${chalk.dim(' project-context.md (merged)')}\n`);
1331
+ }
1332
+
1333
+ /**
1334
+ * Show detailed help for 'set' subcommand
1335
+ */
1336
+ function showHelpSet() {
1337
+ console.log(chalk.bold('\n bmad scope set'));
1338
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1339
+
1340
+ console.log(chalk.bold(' DESCRIPTION\n'));
1341
+ console.log(' Set the active scope for your session. This creates a .bmad-scope file in');
1342
+ console.log(' your project root that workflows automatically detect and use.\n');
1343
+
1344
+ console.log(chalk.bold(' USAGE\n'));
1345
+ console.log(` ${chalk.green('$')} npx bmad-fh scope set [id]`);
1346
+ console.log(` ${chalk.green('$')} npx bmad-fh scope use [id] ${chalk.dim('# alias')}\n`);
1347
+
1348
+ console.log(chalk.bold(' ARGUMENTS\n'));
1349
+ console.log(` ${chalk.cyan('id')} Scope identifier to set as active (optional)`);
1350
+ console.log(' If omitted, shows current scope and prompts to select\n');
1351
+
1352
+ console.log(chalk.bold(' BEHAVIOR\n'));
1353
+ console.log(' • Creates/updates .bmad-scope file in project root');
1354
+ console.log(' • .bmad-scope should be added to .gitignore (session-specific)');
1355
+ console.log(' • Workflows automatically detect scope from this file');
1356
+ console.log(' • BMAD_SCOPE environment variable can override\n');
1357
+
1358
+ console.log(chalk.bold(' EXAMPLES\n'));
1359
+ console.log(chalk.dim(' Set a specific scope:'));
1360
+ console.log(` ${chalk.green('$')} npx bmad-fh scope set auth\n`);
1361
+
1362
+ console.log(chalk.dim(' Interactive selection (shows current and prompts):'));
1363
+ console.log(` ${chalk.green('$')} npx bmad-fh scope set\n`);
1364
+
1365
+ console.log(chalk.dim(' Override with environment variable:'));
1366
+ console.log(` ${chalk.green('$')} BMAD_SCOPE=payments npx bmad-fh ...\n`);
1367
+
1368
+ console.log(chalk.bold(' FILE FORMAT\n'));
1369
+ console.log(' The .bmad-scope file contains:');
1370
+ console.log(chalk.dim(' active_scope: auth'));
1371
+ console.log(chalk.dim(' set_at: "2026-01-22T10:00:00Z"\n'));
1372
+ }
1373
+
1374
+ /**
1375
+ * Show detailed help for 'unset' subcommand
1376
+ */
1377
+ function showHelpUnset() {
1378
+ console.log(chalk.bold('\n bmad scope unset'));
1379
+ console.log(chalk.dim(' ═══════════════════════════════════════════════════════════════════════════\n'));
1380
+
1381
+ console.log(chalk.bold(' DESCRIPTION\n'));
1382
+ console.log(' Clear the active scope by removing the .bmad-scope file. After this,');
1383
+ console.log(' workflows will prompt for scope selection.\n');
1384
+
1385
+ console.log(chalk.bold(' USAGE\n'));
1386
+ console.log(` ${chalk.green('$')} npx bmad-fh scope unset`);
1387
+ console.log(` ${chalk.green('$')} npx bmad-fh scope clear ${chalk.dim('# alias')}\n`);
1388
+
1389
+ console.log(chalk.bold(' BEHAVIOR\n'));
1390
+ console.log(' • Removes .bmad-scope file from project root');
1391
+ console.log(' • Workflows will prompt for scope selection');
1392
+ console.log(' • Does nothing if no scope is currently set\n');
1393
+
1394
+ console.log(chalk.bold(' EXAMPLE\n'));
1395
+ console.log(` ${chalk.green('$')} npx bmad-fh scope unset`);
1396
+ console.log(` ${chalk.dim('✓ Active scope cleared.')}\n`);
1397
+ }
1398
+
1399
+ /**
1400
+ * Router for subcommand-specific help
1401
+ * @param {string} subcommand - The subcommand to show help for
1402
+ */
1403
+ function showSubcommandHelp(subcommand) {
1404
+ const helpFunctions = {
1405
+ init: showHelpInit,
1406
+ create: showHelpCreate,
1407
+ new: showHelpCreate,
1408
+ list: showHelpList,
1409
+ ls: showHelpList,
1410
+ info: showHelpInfo,
1411
+ show: showHelpInfo,
1412
+ remove: showHelpRemove,
1413
+ rm: showHelpRemove,
1414
+ delete: showHelpRemove,
1415
+ archive: showHelpArchive,
1416
+ activate: showHelpActivate,
1417
+ set: showHelpSet,
1418
+ use: showHelpSet,
1419
+ unset: showHelpUnset,
1420
+ clear: showHelpUnset,
1421
+ 'sync-up': showHelpSyncUp,
1422
+ syncup: showHelpSyncUp,
1423
+ 'sync-down': showHelpSyncDown,
1424
+ syncdown: showHelpSyncDown,
1425
+ };
1426
+
1427
+ if (helpFunctions[subcommand]) {
1428
+ helpFunctions[subcommand]();
1429
+ } else {
1430
+ console.log(chalk.red(`\n Unknown command: ${subcommand}\n`));
1431
+ console.log(` Run ${chalk.cyan('npx bmad-fh scope help')} to see available commands.\n`);
1432
+ }
1433
+ }
1434
+
1435
+ /**
1436
+ * Generate help text string for Commander.js
1437
+ * This is called when --help is used
1438
+ */
1439
+ function getHelpText() {
1440
+ const lines = [
1441
+ '',
1442
+ chalk.bold('SUBCOMMANDS'),
1443
+ '',
1444
+ ` ${chalk.cyan('init')} Initialize scope system in current project`,
1445
+ ` ${chalk.cyan('list')} ${chalk.dim('[options]')} List all scopes (aliases: ls)`,
1446
+ ` ${chalk.cyan('create')} ${chalk.dim('[id] [opts]')} Create a new scope (aliases: new)`,
1447
+ ` ${chalk.cyan('info')} ${chalk.dim('<id>')} Show detailed scope information (aliases: show)`,
1448
+ ` ${chalk.cyan('remove')} ${chalk.dim('<id> [opts]')} Remove a scope and its artifacts (aliases: rm, delete)`,
1449
+ ` ${chalk.cyan('archive')} ${chalk.dim('<id>')} Archive a scope (preserves artifacts)`,
1450
+ ` ${chalk.cyan('activate')} ${chalk.dim('<id>')} Reactivate an archived scope`,
1451
+ ` ${chalk.cyan('set')} ${chalk.dim('[id]')} Set active scope for session (alias: use)`,
1452
+ ` ${chalk.cyan('unset')} Clear active scope (alias: clear)`,
1453
+ ` ${chalk.cyan('sync-up')} ${chalk.dim('<id> [opts]')} Promote scope artifacts to shared layer`,
1454
+ ` ${chalk.cyan('sync-down')} ${chalk.dim('<id> [opts]')} Pull shared layer updates into scope`,
1455
+ ` ${chalk.cyan('help')} ${chalk.dim('[command]')} Show detailed help for a command`,
1456
+ '',
1457
+ chalk.bold('QUICK START'),
1458
+ '',
1459
+ ` ${chalk.green('$')} npx bmad-fh scope init`,
1460
+ ` ${chalk.green('$')} npx bmad-fh scope create auth --name "Auth Service"`,
1461
+ ` ${chalk.green('$')} npx bmad-fh scope set auth`,
1462
+ '',
1463
+ chalk.bold('MORE HELP'),
1464
+ '',
1465
+ ` ${chalk.cyan('npx bmad-fh scope help')} Show comprehensive documentation`,
1466
+ ` ${chalk.cyan('npx bmad-fh scope help <cmd>')} Show detailed help for a subcommand`,
1467
+ '',
1468
+ ];
1469
+
1470
+ return lines.join('\n');
1471
+ }
1472
+
1473
+ /**
1474
+ * Configure the Commander command with custom help
1475
+ * @param {import('commander').Command} command - The Commander command instance
1476
+ */
1477
+ function configureCommand(command) {
1478
+ // Add custom help text after the auto-generated options
1479
+ command.addHelpText('after', getHelpText);
1480
+
1481
+ // Show help after errors to guide users
1482
+ command.showHelpAfterError('(use --help for available subcommands)');
388
1483
  }
389
1484
 
390
1485
  module.exports = {
391
1486
  command: 'scope [subcommand] [id]',
392
1487
  description: 'Manage scopes for parallel artifact isolation',
1488
+ configureCommand,
393
1489
  options: [
394
1490
  ['-n, --name <name>', 'Scope name (for create)'],
395
1491
  ['-d, --description <desc>', 'Scope description'],
@@ -398,7 +1494,13 @@ module.exports = {
398
1494
  ['--no-backup', 'Skip backup on remove'],
399
1495
  ['--context', 'Create scope-specific project-context.md'],
400
1496
  ['-s, --status <status>', 'Filter by status (active/archived)'],
1497
+ ['--dry-run', 'Show what would be synced without making changes'],
1498
+ ['--resolution <strategy>', 'Conflict resolution: keep-local|keep-shared|backup-and-update'],
401
1499
  ],
1500
+ // Export help functions for testing
1501
+ showHelp,
1502
+ showSubcommandHelp,
1503
+ getHelpText,
402
1504
  action: async (subcommand, id, options) => {
403
1505
  try {
404
1506
  // Determine project root
@@ -446,7 +1548,40 @@ module.exports = {
446
1548
  break;
447
1549
  }
448
1550
 
449
- case 'help':
1551
+ case 'sync-up':
1552
+ case 'syncup': {
1553
+ await handleSyncUp(projectRoot, id, options);
1554
+ break;
1555
+ }
1556
+
1557
+ case 'sync-down':
1558
+ case 'syncdown': {
1559
+ await handleSyncDown(projectRoot, id, options);
1560
+ break;
1561
+ }
1562
+
1563
+ case 'set':
1564
+ case 'use': {
1565
+ await handleSet(projectRoot, id, options);
1566
+ break;
1567
+ }
1568
+
1569
+ case 'unset':
1570
+ case 'clear': {
1571
+ await handleUnset(projectRoot);
1572
+ break;
1573
+ }
1574
+
1575
+ case 'help': {
1576
+ // Check if a subcommand was provided for detailed help
1577
+ if (id) {
1578
+ showSubcommandHelp(id);
1579
+ } else {
1580
+ showHelp();
1581
+ }
1582
+ break;
1583
+ }
1584
+
450
1585
  case undefined: {
451
1586
  showHelp();
452
1587
  break;