bmad-fh 6.0.0-alpha.23 → 6.0.0-alpha.23.3b00cb36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,35 +28,35 @@ 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
 
34
35
  console.log(chalk.bold('\n Scopes:\n'));
35
-
36
+
36
37
  // Calculate column widths
37
- const idWidth = Math.max(10, ...scopes.map(s => s.id.length)) + 2;
38
- const nameWidth = Math.max(15, ...scopes.map(s => (s.name || '').length)) + 2;
39
-
38
+ const idWidth = Math.max(10, ...scopes.map((s) => s.id.length)) + 2;
39
+ const nameWidth = Math.max(15, ...scopes.map((s) => (s.name || '').length)) + 2;
40
+
40
41
  // Header
41
42
  console.log(
42
43
  chalk.dim(' ') +
43
- chalk.bold('ID'.padEnd(idWidth)) +
44
- chalk.bold('Name'.padEnd(nameWidth)) +
45
- chalk.bold('Status'.padEnd(10)) +
46
- chalk.bold('Created')
44
+ chalk.bold('ID'.padEnd(idWidth)) +
45
+ chalk.bold('Name'.padEnd(nameWidth)) +
46
+ chalk.bold('Status'.padEnd(10)) +
47
+ chalk.bold('Created'),
47
48
  );
48
49
  console.log(chalk.dim(' ' + '─'.repeat(idWidth + nameWidth + 10 + 20)));
49
-
50
+
50
51
  // Rows
51
52
  for (const scope of scopes) {
52
53
  const statusColor = scope.status === 'active' ? chalk.green : chalk.dim;
53
54
  console.log(
54
55
  ' ' +
55
- chalk.cyan(scope.id.padEnd(idWidth)) +
56
- (scope.name || scope.id).padEnd(nameWidth) +
57
- statusColor(scope.status.padEnd(10)) +
58
- chalk.dim(formatDate(scope.created).split(' ')[0])
56
+ chalk.cyan(scope.id.padEnd(idWidth)) +
57
+ (scope.name || scope.id).padEnd(nameWidth) +
58
+ statusColor(scope.status.padEnd(10)) +
59
+ chalk.dim(formatDate(scope.created).split(' ')[0]),
59
60
  );
60
61
  }
61
62
  console.log();
@@ -69,7 +70,7 @@ function displayScopeList(scopes) {
69
70
  */
70
71
  function displayScopeInfo(scope, paths, tree) {
71
72
  console.log(chalk.bold(`\n Scope: ${scope.name || scope.id}\n`));
72
-
73
+
73
74
  console.log(chalk.dim(' ─────────────────────────────────────'));
74
75
  console.log(` ${chalk.bold('ID:')} ${chalk.cyan(scope.id)}`);
75
76
  console.log(` ${chalk.bold('Name:')} ${scope.name || 'N/A'}`);
@@ -78,13 +79,13 @@ function displayScopeInfo(scope, paths, tree) {
78
79
  console.log(` ${chalk.bold('Created:')} ${formatDate(scope.created)}`);
79
80
  console.log(` ${chalk.bold('Last Active:')} ${formatDate(scope._meta?.last_activity)}`);
80
81
  console.log(` ${chalk.bold('Artifacts:')} ${scope._meta?.artifact_count || 0}`);
81
-
82
+
82
83
  console.log(chalk.dim('\n ─────────────────────────────────────'));
83
84
  console.log(chalk.bold(' Paths:'));
84
85
  console.log(` Planning: ${chalk.dim(paths.planning)}`);
85
86
  console.log(` Implementation: ${chalk.dim(paths.implementation)}`);
86
87
  console.log(` Tests: ${chalk.dim(paths.tests)}`);
87
-
88
+
88
89
  console.log(chalk.dim('\n ─────────────────────────────────────'));
89
90
  console.log(chalk.bold(' Dependencies:'));
90
91
  if (tree.dependencies.length > 0) {
@@ -95,7 +96,7 @@ function displayScopeInfo(scope, paths, tree) {
95
96
  } else {
96
97
  console.log(chalk.dim(' None'));
97
98
  }
98
-
99
+
99
100
  console.log(chalk.bold('\n Dependents (scopes that depend on this):'));
100
101
  if (tree.dependents.length > 0) {
101
102
  for (const dep of tree.dependents) {
@@ -104,7 +105,7 @@ function displayScopeInfo(scope, paths, tree) {
104
105
  } else {
105
106
  console.log(chalk.dim(' None'));
106
107
  }
107
-
108
+
108
109
  console.log();
109
110
  }
110
111
 
@@ -113,14 +114,24 @@ function displayScopeInfo(scope, paths, tree) {
113
114
  */
114
115
  async function handleList(projectRoot, options) {
115
116
  const manager = new ScopeManager({ projectRoot });
116
-
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
+ }
127
+
117
128
  try {
118
129
  await manager.initialize();
119
130
  const scopes = await manager.listScopes(options.status ? { status: options.status } : {});
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
  }
@@ -134,7 +145,7 @@ async function handleCreate(projectRoot, scopeId, options) {
134
145
  const manager = new ScopeManager({ projectRoot });
135
146
  const initializer = new ScopeInitializer({ projectRoot });
136
147
  const validator = new ScopeValidator();
137
-
148
+
138
149
  // If no scopeId provided, prompt for it
139
150
  if (!scopeId) {
140
151
  const inputId = await text({
@@ -143,87 +154,85 @@ async function handleCreate(projectRoot, scopeId, options) {
143
154
  validate: (value) => {
144
155
  const result = validator.validateScopeId(value);
145
156
  return result.valid ? undefined : result.error;
146
- }
157
+ },
147
158
  });
148
-
159
+
149
160
  if (isCancel(inputId)) {
150
161
  console.log(chalk.yellow('Cancelled.'));
151
162
  return;
152
163
  }
153
164
  scopeId = inputId;
154
165
  }
155
-
166
+
156
167
  // Validate scope ID
157
168
  const idValidation = validator.validateScopeId(scopeId);
158
169
  if (!idValidation.valid) {
159
170
  console.error(chalk.red(`Error: ${idValidation.error}`));
160
171
  process.exit(1);
161
172
  }
162
-
173
+
163
174
  // Get scope name if not provided
164
175
  let name = options.name;
165
176
  if (!name) {
166
177
  const inputName = await text({
167
178
  message: 'Enter scope name (human-readable):',
168
179
  placeholder: `e.g., Authentication Service`,
169
- initialValue: scopeId.charAt(0).toUpperCase() + scopeId.slice(1).replaceAll('-', ' ')
180
+ initialValue: scopeId.charAt(0).toUpperCase() + scopeId.slice(1).replaceAll('-', ' '),
170
181
  });
171
-
182
+
172
183
  if (isCancel(inputName)) {
173
184
  console.log(chalk.yellow('Cancelled.'));
174
185
  return;
175
186
  }
176
187
  name = inputName;
177
188
  }
178
-
179
- // Get description if not provided
189
+
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
- placeholder: 'Brief description of this scope'
195
+ placeholder: 'Brief description of this scope',
185
196
  });
186
-
197
+
187
198
  if (isCancel(inputDesc)) {
188
199
  console.log(chalk.yellow('Cancelled.'));
189
200
  return;
190
201
  }
191
202
  description = inputDesc || '';
192
203
  }
193
-
204
+
194
205
  console.log(chalk.blue('\nCreating scope...'));
195
-
206
+
196
207
  // Initialize scope system if needed
197
208
  await manager.initialize();
198
-
209
+
199
210
  // Check if system is initialized
200
211
  const systemInit = await initializer.isSystemInitialized();
201
212
  if (!systemInit) {
202
213
  console.log(chalk.blue('Initializing scope system...'));
203
214
  await initializer.initializeScopeSystem();
204
215
  }
205
-
206
- // Create scope in configuration
216
+
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
- dependencies: options.dependencies ? options.dependencies.split(',').map(d => d.trim()) : []
222
+ dependencies: options.dependencies ? options.dependencies.split(',').map((d) => d.trim()) : [],
223
+ createContext: options.context,
211
224
  });
212
-
213
- // Create scope directory structure
214
- const paths = await initializer.initializeScope(scopeId, {
215
- name,
216
- description,
217
- createContext: options.context
218
- });
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,23 +241,23 @@ 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
+
239
248
  const manager = new ScopeManager({ projectRoot });
240
-
249
+
241
250
  await manager.initialize();
242
251
  const scope = await manager.getScope(scopeId);
243
-
252
+
244
253
  if (!scope) {
245
254
  console.error(chalk.red(`Error: Scope '${scopeId}' not found.`));
246
255
  process.exit(1);
247
256
  }
248
-
257
+
249
258
  const paths = await manager.getScopePaths(scopeId);
250
259
  const tree = await manager.getDependencyTree(scopeId);
251
-
260
+
252
261
  displayScopeInfo(scope, paths, tree);
253
262
  }
254
263
 
@@ -257,44 +266,46 @@ 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
+
264
273
  const manager = new ScopeManager({ projectRoot });
265
274
  const initializer = new ScopeInitializer({ projectRoot });
266
-
275
+
267
276
  await manager.initialize();
268
-
277
+
269
278
  const scope = await manager.getScope(scopeId);
270
279
  if (!scope) {
271
280
  console.error(chalk.red(`Error: Scope '${scopeId}' not found.`));
272
281
  process.exit(1);
273
282
  }
274
-
283
+
275
284
  // Confirm removal unless --force
276
285
  if (!options.force) {
277
286
  const confirmed = await confirm({
278
287
  message: `Are you sure you want to remove scope '${scopeId}'? This will delete all scope artifacts!`,
279
- initialValue: false
288
+ initialValue: false,
280
289
  });
281
-
290
+
282
291
  if (isCancel(confirmed) || !confirmed) {
283
292
  console.log(chalk.yellow('Cancelled.'));
284
293
  return;
285
294
  }
286
295
  }
287
-
296
+
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 });
292
-
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 });
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();
@@ -306,19 +317,19 @@ async function handleRemove(projectRoot, scopeId, options) {
306
317
  async function handleInit(projectRoot) {
307
318
  const manager = new ScopeManager({ projectRoot });
308
319
  const initializer = new ScopeInitializer({ projectRoot });
309
-
320
+
310
321
  console.log(chalk.blue('\nInitializing scope system...'));
311
-
322
+
312
323
  await manager.initialize();
313
324
  await initializer.initializeScopeSystem();
314
-
325
+
315
326
  console.log(chalk.green('\n✓ Scope system initialized successfully!\n'));
316
327
  console.log(chalk.dim(' Created:'));
317
328
  console.log(` ${chalk.cyan('_bmad/_config/scopes.yaml')} - Scope configuration`);
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,15 +338,15 @@ 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
+
334
345
  const manager = new ScopeManager({ projectRoot });
335
-
346
+
336
347
  await manager.initialize();
337
348
  await manager.archiveScope(scopeId);
338
-
349
+
339
350
  console.log(chalk.green(`\n✓ Scope '${scopeId}' archived.\n`));
340
351
  }
341
352
 
@@ -344,52 +355,1137 @@ 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
+
351
362
  const manager = new ScopeManager({ projectRoot });
352
-
363
+
353
364
  await manager.initialize();
354
365
  await manager.activateScope(scopeId);
355
-
366
+
356
367
  console.log(chalk.green(`\n✓ Scope '${scopeId}' activated.\n`));
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'],
@@ -397,61 +1493,100 @@ module.exports = {
397
1493
  ['-f, --force', 'Force operation without confirmation'],
398
1494
  ['--no-backup', 'Skip backup on remove'],
399
1495
  ['--context', 'Create scope-specific project-context.md'],
400
- ['-s, --status <status>', 'Filter by status (active/archived)']
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
405
1507
  const projectRoot = process.cwd();
406
-
1508
+
407
1509
  // Handle subcommands
408
1510
  switch (subcommand) {
409
1511
  case 'init': {
410
1512
  await handleInit(projectRoot);
411
1513
  break;
412
1514
  }
413
-
1515
+
414
1516
  case 'list':
415
1517
  case 'ls': {
416
1518
  await handleList(projectRoot, options);
417
1519
  break;
418
1520
  }
419
-
1521
+
420
1522
  case 'create':
421
1523
  case 'new': {
422
1524
  await handleCreate(projectRoot, id, options);
423
1525
  break;
424
1526
  }
425
-
1527
+
426
1528
  case 'info':
427
1529
  case 'show': {
428
1530
  await handleInfo(projectRoot, id);
429
1531
  break;
430
1532
  }
431
-
1533
+
432
1534
  case 'remove':
433
1535
  case 'rm':
434
1536
  case 'delete': {
435
1537
  await handleRemove(projectRoot, id, options);
436
1538
  break;
437
1539
  }
438
-
1540
+
439
1541
  case 'archive': {
440
1542
  await handleArchive(projectRoot, id);
441
1543
  break;
442
1544
  }
443
-
1545
+
444
1546
  case 'activate': {
445
1547
  await handleActivate(projectRoot, id);
446
1548
  break;
447
1549
  }
448
-
449
- case 'help':
1550
+
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;
453
1588
  }
454
-
1589
+
455
1590
  default: {
456
1591
  // If subcommand looks like an ID, show info for it
457
1592
  if (subcommand && !subcommand.startsWith('-')) {
@@ -461,7 +1596,7 @@ module.exports = {
461
1596
  }
462
1597
  }
463
1598
  }
464
-
1599
+
465
1600
  process.exit(0);
466
1601
  } catch (error) {
467
1602
  console.error(chalk.red(`\nError: ${error.message}`));
@@ -470,5 +1605,5 @@ module.exports = {
470
1605
  }
471
1606
  process.exit(1);
472
1607
  }
473
- }
1608
+ },
474
1609
  };