agileflow 2.51.0 → 2.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +82 -460
  2. package/package.json +18 -3
  3. package/scripts/agileflow-configure.js +134 -63
  4. package/scripts/agileflow-welcome.js +161 -31
  5. package/scripts/generators/agent-registry.js +2 -2
  6. package/scripts/generators/command-registry.js +6 -6
  7. package/scripts/generators/index.js +2 -6
  8. package/scripts/generators/inject-babysit.js +9 -2
  9. package/scripts/generators/inject-help.js +3 -1
  10. package/scripts/generators/inject-readme.js +7 -3
  11. package/scripts/generators/skill-registry.js +5 -5
  12. package/scripts/get-env.js +13 -12
  13. package/scripts/obtain-context.js +79 -26
  14. package/scripts/session-coordinator.sh +232 -0
  15. package/scripts/session-manager.js +512 -0
  16. package/src/core/agents/orchestrator.md +275 -0
  17. package/src/core/commands/adr.md +38 -16
  18. package/src/core/commands/agent.md +39 -22
  19. package/src/core/commands/assign.md +17 -0
  20. package/src/core/commands/auto.md +60 -46
  21. package/src/core/commands/babysit.md +302 -637
  22. package/src/core/commands/baseline.md +20 -0
  23. package/src/core/commands/blockers.md +33 -48
  24. package/src/core/commands/board.md +19 -0
  25. package/src/core/commands/changelog.md +20 -0
  26. package/src/core/commands/ci.md +17 -0
  27. package/src/core/commands/context.md +43 -40
  28. package/src/core/commands/debt.md +76 -45
  29. package/src/core/commands/deploy.md +20 -0
  30. package/src/core/commands/deps.md +40 -46
  31. package/src/core/commands/diagnose.md +24 -18
  32. package/src/core/commands/docs.md +18 -0
  33. package/src/core/commands/epic.md +31 -0
  34. package/src/core/commands/feedback.md +33 -21
  35. package/src/core/commands/handoff.md +29 -0
  36. package/src/core/commands/help.md +16 -7
  37. package/src/core/commands/impact.md +31 -61
  38. package/src/core/commands/metrics.md +17 -35
  39. package/src/core/commands/packages.md +21 -0
  40. package/src/core/commands/pr.md +15 -0
  41. package/src/core/commands/readme-sync.md +42 -9
  42. package/src/core/commands/research.md +58 -11
  43. package/src/core/commands/retro.md +42 -50
  44. package/src/core/commands/review.md +22 -27
  45. package/src/core/commands/session/end.md +53 -297
  46. package/src/core/commands/session/history.md +38 -257
  47. package/src/core/commands/session/init.md +44 -446
  48. package/src/core/commands/session/new.md +152 -0
  49. package/src/core/commands/session/resume.md +51 -447
  50. package/src/core/commands/session/status.md +32 -244
  51. package/src/core/commands/sprint.md +33 -0
  52. package/src/core/commands/status.md +18 -0
  53. package/src/core/commands/story-validate.md +32 -0
  54. package/src/core/commands/story.md +21 -6
  55. package/src/core/commands/template.md +18 -0
  56. package/src/core/commands/tests.md +22 -0
  57. package/src/core/commands/update.md +72 -58
  58. package/src/core/commands/validate-expertise.md +25 -37
  59. package/src/core/commands/velocity.md +33 -74
  60. package/src/core/commands/verify.md +16 -0
  61. package/src/core/experts/documentation/expertise.yaml +16 -2
  62. package/src/core/skills/agileflow-retro-facilitator/SKILL.md +57 -219
  63. package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +86 -0
  64. package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +79 -0
  65. package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +142 -0
  66. package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +83 -0
  67. package/src/core/skills/writing-skills/SKILL.md +352 -0
  68. package/src/core/skills/writing-skills/testing-skills-with-subagents.md +232 -0
  69. package/tools/cli/agileflow-cli.js +4 -2
  70. package/tools/cli/commands/config.js +20 -13
  71. package/tools/cli/commands/doctor.js +25 -9
  72. package/tools/cli/commands/list.js +10 -6
  73. package/tools/cli/commands/setup.js +54 -3
  74. package/tools/cli/commands/status.js +6 -8
  75. package/tools/cli/commands/uninstall.js +5 -5
  76. package/tools/cli/commands/update.js +51 -7
  77. package/tools/cli/installers/core/installer.js +8 -4
  78. package/tools/cli/installers/ide/_base-ide.js +3 -1
  79. package/tools/cli/installers/ide/claude-code.js +3 -7
  80. package/tools/cli/installers/ide/codex.js +440 -0
  81. package/tools/cli/installers/ide/manager.js +2 -6
  82. package/tools/cli/lib/content-injector.js +3 -3
  83. package/tools/cli/lib/docs-setup.js +3 -2
  84. package/tools/cli/lib/npm-utils.js +3 -3
  85. package/tools/cli/lib/ui.js +7 -7
  86. package/tools/cli/lib/version-checker.js +3 -3
  87. package/tools/postinstall.js +2 -3
@@ -13,7 +13,10 @@
13
13
 
14
14
  const fs = require('fs');
15
15
  const path = require('path');
16
- const { execSync } = require('child_process');
16
+ const { execSync, spawnSync } = require('child_process');
17
+
18
+ // Session manager path (relative to script location)
19
+ const SESSION_MANAGER_PATH = path.join(__dirname, 'session-manager.js');
17
20
 
18
21
  // ANSI color codes
19
22
  const c = {
@@ -39,9 +42,16 @@ const c = {
39
42
 
40
43
  // Box drawing characters
41
44
  const box = {
42
- tl: '╭', tr: '╮', bl: '╰', br: '╯',
43
- h: '', v: '│',
44
- lT: '', rT: '┤', tT: '┬', bT: '┴',
45
+ tl: '╭',
46
+ tr: '',
47
+ bl: '',
48
+ br: '╯',
49
+ h: '─',
50
+ v: '│',
51
+ lT: '├',
52
+ rT: '┤',
53
+ tT: '┬',
54
+ bT: '┴',
45
55
  cross: '┼',
46
56
  };
47
57
 
@@ -70,7 +80,9 @@ function getProjectInfo(rootDir) {
70
80
 
71
81
  // Get package info
72
82
  try {
73
- const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, 'packages/cli/package.json'), 'utf8'));
83
+ const pkg = JSON.parse(
84
+ fs.readFileSync(path.join(rootDir, 'packages/cli/package.json'), 'utf8')
85
+ );
74
86
  info.version = pkg.version || info.version;
75
87
  } catch (e) {
76
88
  try {
@@ -83,7 +95,10 @@ function getProjectInfo(rootDir) {
83
95
  try {
84
96
  info.branch = execSync('git branch --show-current', { cwd: rootDir, encoding: 'utf8' }).trim();
85
97
  info.commit = execSync('git rev-parse --short HEAD', { cwd: rootDir, encoding: 'utf8' }).trim();
86
- info.lastCommit = execSync('git log -1 --format="%s"', { cwd: rootDir, encoding: 'utf8' }).trim();
98
+ info.lastCommit = execSync('git log -1 --format="%s"', {
99
+ cwd: rootDir,
100
+ encoding: 'utf8',
101
+ }).trim();
87
102
  } catch (e) {}
88
103
 
89
104
  // Get status info
@@ -155,7 +170,7 @@ function runArchival(rootDir) {
155
170
  execSync('bash scripts/archive-completed-stories.sh', {
156
171
  cwd: rootDir,
157
172
  encoding: 'utf8',
158
- stdio: 'pipe'
173
+ stdio: 'pipe',
159
174
  });
160
175
  result.archived = toArchiveCount;
161
176
  result.remaining -= toArchiveCount;
@@ -201,6 +216,60 @@ function clearActiveCommands(rootDir) {
201
216
  return result;
202
217
  }
203
218
 
219
+ function checkParallelSessions(rootDir) {
220
+ const result = {
221
+ available: false,
222
+ registered: false,
223
+ otherActive: 0,
224
+ currentId: null,
225
+ cleaned: 0,
226
+ };
227
+
228
+ try {
229
+ // Check if session manager exists
230
+ const managerPath = path.join(rootDir, '.agileflow', 'scripts', 'session-manager.js');
231
+ if (!fs.existsSync(managerPath) && !fs.existsSync(SESSION_MANAGER_PATH)) {
232
+ return result;
233
+ }
234
+
235
+ result.available = true;
236
+
237
+ // Try to register current session and get status
238
+ const scriptPath = fs.existsSync(managerPath) ? managerPath : SESSION_MANAGER_PATH;
239
+
240
+ // Register this session
241
+ try {
242
+ const registerOutput = execSync(`node "${scriptPath}" register`, {
243
+ cwd: rootDir,
244
+ encoding: 'utf8',
245
+ stdio: ['pipe', 'pipe', 'pipe'],
246
+ });
247
+ const registerData = JSON.parse(registerOutput);
248
+ result.registered = true;
249
+ result.currentId = registerData.id;
250
+ } catch (e) {
251
+ // Registration failed, continue anyway
252
+ }
253
+
254
+ // Get count of other active sessions
255
+ try {
256
+ const countOutput = execSync(`node "${scriptPath}" count`, {
257
+ cwd: rootDir,
258
+ encoding: 'utf8',
259
+ stdio: ['pipe', 'pipe', 'pipe'],
260
+ });
261
+ const countData = JSON.parse(countOutput);
262
+ result.otherActive = countData.count || 0;
263
+ } catch (e) {
264
+ // Count failed
265
+ }
266
+ } catch (e) {
267
+ // Session system not available
268
+ }
269
+
270
+ return result;
271
+ }
272
+
204
273
  function checkPreCompact(rootDir) {
205
274
  const result = { configured: false, scriptExists: false, version: null, outdated: false };
206
275
 
@@ -258,7 +327,7 @@ function getFeatureVersions(rootDir) {
258
327
  hooks: { version: null, outdated: false },
259
328
  archival: { version: null, outdated: false },
260
329
  statusline: { version: null, outdated: false },
261
- precompact: { version: null, outdated: false }
330
+ precompact: { version: null, outdated: false },
262
331
  };
263
332
 
264
333
  // Minimum compatible versions for each feature
@@ -266,7 +335,7 @@ function getFeatureVersions(rootDir) {
266
335
  hooks: '2.35.0',
267
336
  archival: '2.35.0',
268
337
  statusline: '2.35.0',
269
- precompact: '2.40.0' // Multi-command support
338
+ precompact: '2.40.0', // Multi-command support
270
339
  };
271
340
 
272
341
  try {
@@ -277,7 +346,8 @@ function getFeatureVersions(rootDir) {
277
346
  for (const feature of Object.keys(result)) {
278
347
  if (metadata.features?.[feature]?.configured_version) {
279
348
  result[feature].version = metadata.features[feature].configured_version;
280
- result[feature].outdated = compareVersions(result[feature].version, minVersions[feature]) < 0;
349
+ result[feature].outdated =
350
+ compareVersions(result[feature].version, minVersions[feature]) < 0;
281
351
  }
282
352
  }
283
353
  }
@@ -291,7 +361,8 @@ function pad(str, len, align = 'left') {
291
361
  const diff = len - stripped.length;
292
362
  if (diff <= 0) return str;
293
363
  if (align === 'right') return ' '.repeat(diff) + str;
294
- if (align === 'center') return ' '.repeat(Math.floor(diff/2)) + str + ' '.repeat(Math.ceil(diff/2));
364
+ if (align === 'center')
365
+ return ' '.repeat(Math.floor(diff / 2)) + str + ' '.repeat(Math.ceil(diff / 2));
295
366
  return str + ' '.repeat(diff);
296
367
  }
297
368
 
@@ -323,7 +394,7 @@ function truncate(str, maxLen, suffix = '..') {
323
394
  return str.substring(0, cutIndex) + suffix;
324
395
  }
325
396
 
326
- function formatTable(info, archival, session, precompact) {
397
+ function formatTable(info, archival, session, precompact, parallelSessions) {
327
398
  const W = 58; // inner width
328
399
  const R = W - 24; // right column width (34 chars)
329
400
  const lines = [];
@@ -336,17 +407,20 @@ function formatTable(info, archival, session, precompact) {
336
407
  return `${c.dim}${box.v}${c.reset} ${pad(leftStr, 20)} ${c.dim}${box.v}${c.reset} ${pad(rightStr, R)} ${c.dim}${box.v}${c.reset}`;
337
408
  };
338
409
 
339
- const divider = () => `${c.dim}${box.lT}${box.h.repeat(22)}${box.cross}${box.h.repeat(W - 22)}${box.rT}${c.reset}`;
410
+ const divider = () =>
411
+ `${c.dim}${box.lT}${box.h.repeat(22)}${box.cross}${box.h.repeat(W - 22)}${box.rT}${c.reset}`;
340
412
  const topBorder = `${c.dim}${box.tl}${box.h.repeat(22)}${box.tT}${box.h.repeat(W - 22)}${box.tr}${c.reset}`;
341
413
  const bottomBorder = `${c.dim}${box.bl}${box.h.repeat(22)}${box.bT}${box.h.repeat(W - 22)}${box.br}${c.reset}`;
342
414
 
343
415
  // Header (truncate branch name if too long)
344
- const branchColor = info.branch === 'main' ? c.green : info.branch.startsWith('fix') ? c.red : c.cyan;
416
+ const branchColor =
417
+ info.branch === 'main' ? c.green : info.branch.startsWith('fix') ? c.red : c.cyan;
345
418
  // Fixed parts: "agileflow " (10) + "v" (1) + version + " " (2) + " (" (2) + commit (7) + ")" (1) = 23 + version.length
346
- const maxBranchLen = (W - 1) - 23 - info.version.length;
347
- const branchDisplay = info.branch.length > maxBranchLen
348
- ? info.branch.substring(0, maxBranchLen - 2) + '..'
349
- : info.branch;
419
+ const maxBranchLen = W - 1 - 23 - info.version.length;
420
+ const branchDisplay =
421
+ info.branch.length > maxBranchLen
422
+ ? info.branch.substring(0, maxBranchLen - 2) + '..'
423
+ : info.branch;
350
424
  const header = `${c.brand}${c.bold}agileflow${c.reset} ${c.dim}v${info.version}${c.reset} ${branchColor}${branchDisplay}${c.reset} ${c.dim}(${info.commit})${c.reset}`;
351
425
  const headerLine = `${c.dim}${box.v}${c.reset} ${pad(header, W - 1)} ${c.dim}${box.v}${c.reset}`;
352
426
 
@@ -355,10 +429,38 @@ function formatTable(info, archival, session, precompact) {
355
429
  lines.push(divider());
356
430
 
357
431
  // Stories section
358
- lines.push(row('In Progress', info.wipCount > 0 ? `${info.wipCount}` : '0', c.dim, info.wipCount > 0 ? c.yellow : c.dim));
359
- lines.push(row('Blocked', info.blockedCount > 0 ? `${info.blockedCount}` : '0', c.dim, info.blockedCount > 0 ? c.red : c.dim));
360
- lines.push(row('Ready', info.readyCount > 0 ? `${info.readyCount}` : '0', c.dim, info.readyCount > 0 ? c.cyan : c.dim));
361
- lines.push(row('Completed', info.completedCount > 0 ? `${info.completedCount}` : '0', c.dim, info.completedCount > 0 ? c.green : c.dim));
432
+ lines.push(
433
+ row(
434
+ 'In Progress',
435
+ info.wipCount > 0 ? `${info.wipCount}` : '0',
436
+ c.dim,
437
+ info.wipCount > 0 ? c.yellow : c.dim
438
+ )
439
+ );
440
+ lines.push(
441
+ row(
442
+ 'Blocked',
443
+ info.blockedCount > 0 ? `${info.blockedCount}` : '0',
444
+ c.dim,
445
+ info.blockedCount > 0 ? c.red : c.dim
446
+ )
447
+ );
448
+ lines.push(
449
+ row(
450
+ 'Ready',
451
+ info.readyCount > 0 ? `${info.readyCount}` : '0',
452
+ c.dim,
453
+ info.readyCount > 0 ? c.cyan : c.dim
454
+ )
455
+ );
456
+ lines.push(
457
+ row(
458
+ 'Completed',
459
+ info.completedCount > 0 ? `${info.completedCount}` : '0',
460
+ c.dim,
461
+ info.completedCount > 0 ? c.green : c.dim
462
+ )
463
+ );
362
464
 
363
465
  lines.push(divider());
364
466
 
@@ -366,16 +468,15 @@ function formatTable(info, archival, session, precompact) {
366
468
  if (archival.disabled) {
367
469
  lines.push(row('Auto-archival', 'disabled', c.dim, c.dim));
368
470
  } else {
369
- const archivalStatus = archival.archived > 0
370
- ? `archived ${archival.archived} stories`
371
- : `nothing to archive`;
372
- lines.push(row('Auto-archival', archivalStatus, c.dim, archival.archived > 0 ? c.green : c.dim));
471
+ const archivalStatus =
472
+ archival.archived > 0 ? `archived ${archival.archived} stories` : `nothing to archive`;
473
+ lines.push(
474
+ row('Auto-archival', archivalStatus, c.dim, archival.archived > 0 ? c.green : c.dim)
475
+ );
373
476
  }
374
477
 
375
478
  // Session cleanup
376
- const sessionStatus = session.cleared > 0
377
- ? `cleared ${session.cleared} command(s)`
378
- : `clean`;
479
+ const sessionStatus = session.cleared > 0 ? `cleared ${session.cleared} command(s)` : `clean`;
379
480
  lines.push(row('Session state', sessionStatus, c.dim, session.cleared > 0 ? c.green : c.dim));
380
481
 
381
482
  // PreCompact status with version check
@@ -396,11 +497,31 @@ function formatTable(info, archival, session, precompact) {
396
497
  lines.push(row('Context preserve', 'not configured', c.dim, c.dim));
397
498
  }
398
499
 
500
+ // Parallel sessions status
501
+ if (parallelSessions && parallelSessions.available) {
502
+ if (parallelSessions.otherActive > 0) {
503
+ const sessionStr = `⚠️ ${parallelSessions.otherActive} other active`;
504
+ lines.push(row('Sessions', sessionStr, c.dim, c.yellow));
505
+ } else {
506
+ const sessionStr = parallelSessions.currentId
507
+ ? `✓ Session ${parallelSessions.currentId} (only)`
508
+ : '✓ Only session';
509
+ lines.push(row('Sessions', sessionStr, c.dim, c.green));
510
+ }
511
+ }
512
+
399
513
  lines.push(divider());
400
514
 
401
515
  // Current story (if any) - row() auto-truncates
402
516
  if (info.currentStory) {
403
- lines.push(row('Current', `${c.blue}${info.currentStory.id}${c.reset}: ${info.currentStory.title}`, c.dim, ''));
517
+ lines.push(
518
+ row(
519
+ 'Current',
520
+ `${c.blue}${info.currentStory.id}${c.reset}: ${info.currentStory.title}`,
521
+ c.dim,
522
+ ''
523
+ )
524
+ );
404
525
  } else {
405
526
  lines.push(row('Current', 'No active story', c.dim, c.dim));
406
527
  }
@@ -420,8 +541,17 @@ function main() {
420
541
  const archival = runArchival(rootDir);
421
542
  const session = clearActiveCommands(rootDir);
422
543
  const precompact = checkPreCompact(rootDir);
544
+ const parallelSessions = checkParallelSessions(rootDir);
545
+
546
+ console.log(formatTable(info, archival, session, precompact, parallelSessions));
423
547
 
424
- console.log(formatTable(info, archival, session, precompact));
548
+ // Show warning and tip if other sessions are active
549
+ if (parallelSessions.otherActive > 0) {
550
+ console.log('');
551
+ console.log(`${c.yellow}⚠️ Other Claude session(s) active in this repo.${c.reset}`);
552
+ console.log(`${c.dim} Run /agileflow:session:status to see all sessions.${c.reset}`);
553
+ console.log(`${c.dim} Run /agileflow:session:new to create isolated workspace.${c.reset}`);
554
+ }
425
555
  }
426
556
 
427
557
  main();
@@ -74,7 +74,7 @@ function categorizeAgent(name, description) {
74
74
  'Maintenance & Optimization': ['refactor', 'performance', 'monitoring'],
75
75
  'Documentation & Knowledge': ['documentation', 'readme-updater', 'research'],
76
76
  'Compliance & Governance': ['compliance', 'analytics'],
77
- 'Mentorship': ['mentor']
77
+ Mentorship: ['mentor'],
78
78
  };
79
79
 
80
80
  for (const [category, keywords] of Object.entries(categories)) {
@@ -121,7 +121,7 @@ function scanAgents(agentsDir) {
121
121
  tools,
122
122
  model: frontmatter.model || 'haiku',
123
123
  color: frontmatter.color || 'blue',
124
- category: categorizeAgent(name, frontmatter.description || '')
124
+ category: categorizeAgent(name, frontmatter.description || ''),
125
125
  });
126
126
  }
127
127
 
@@ -47,15 +47,15 @@ function extractFrontmatter(filePath) {
47
47
  function categorizeCommand(name, description) {
48
48
  const categories = {
49
49
  'Story Management': ['story', 'epic', 'assign', 'status'],
50
- 'Development': ['verify', 'baseline', 'resume', 'session-init', 'babysit'],
50
+ Development: ['verify', 'baseline', 'resume', 'session-init', 'babysit'],
51
51
  'Quality & Testing': ['tests', 'review', 'ci'],
52
- 'Documentation': ['docs', 'adr', 'readme-sync'],
52
+ Documentation: ['docs', 'adr', 'readme-sync'],
53
53
  'Planning & Metrics': ['sprint', 'velocity', 'metrics', 'board', 'deps'],
54
54
  'Research & Strategy': ['research', 'product'],
55
55
  'Deployment & Operations': ['deploy', 'packages'],
56
- 'Collaboration': ['update', 'handoff', 'feedback', 'retro'],
57
- 'Maintenance': ['debt', 'compress', 'template'],
58
- 'System': ['setup', 'help', 'diagnose', 'auto', 'agent']
56
+ Collaboration: ['update', 'handoff', 'feedback', 'retro'],
57
+ Maintenance: ['debt', 'compress', 'template'],
58
+ System: ['setup', 'help', 'diagnose', 'auto', 'agent'],
59
59
  };
60
60
 
61
61
  for (const [category, keywords] of Object.entries(categories)) {
@@ -89,7 +89,7 @@ function scanCommands(commandsDir) {
89
89
  path: filePath,
90
90
  description: frontmatter.description || '',
91
91
  argumentHint: frontmatter['argument-hint'] || '',
92
- category: categorizeCommand(name, frontmatter.description || '')
92
+ category: categorizeCommand(name, frontmatter.description || ''),
93
93
  });
94
94
  }
95
95
 
@@ -25,7 +25,7 @@ function runGenerator(scriptName) {
25
25
  try {
26
26
  execSync(`node "${scriptPath}"`, {
27
27
  cwd: __dirname,
28
- stdio: 'inherit'
28
+ stdio: 'inherit',
29
29
  });
30
30
  console.log(`✅ ${scriptName} completed successfully`);
31
31
  return true;
@@ -42,11 +42,7 @@ function main() {
42
42
  console.log('🚀 AgileFlow Content Generation System');
43
43
  console.log('Generating content from metadata...\n');
44
44
 
45
- const generators = [
46
- 'inject-help.js',
47
- 'inject-babysit.js',
48
- 'inject-readme.js'
49
- ];
45
+ const generators = ['inject-help.js', 'inject-babysit.js', 'inject-readme.js'];
50
46
 
51
47
  const results = [];
52
48
 
@@ -83,7 +83,9 @@ function injectContentByMarker(content, markerName, generated) {
83
83
  const timestamp = new Date().toISOString().split('T')[0];
84
84
  const injectedContent = `${startMarker}\n<!-- Auto-generated on ${timestamp}. Do not edit manually. -->\n\n${generated}\n${endMarker}`;
85
85
 
86
- return content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length);
86
+ return (
87
+ content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length)
88
+ );
87
89
  }
88
90
 
89
91
  /**
@@ -159,7 +161,12 @@ function main() {
159
161
  }
160
162
 
161
163
  // Export for use in orchestrator
162
- module.exports = { generateAgentList, generateCommandReference, injectContentByMarker, addMarkersIfMissing };
164
+ module.exports = {
165
+ generateAgentList,
166
+ generateCommandReference,
167
+ injectContentByMarker,
168
+ addMarkersIfMissing,
169
+ };
163
170
 
164
171
  // Run if called directly
165
172
  if (require.main === module) {
@@ -62,7 +62,9 @@ function injectContent(content, generated) {
62
62
  const timestamp = new Date().toISOString().split('T')[0];
63
63
  const injectedContent = `${startMarker}\n<!-- Auto-generated on ${timestamp}. Do not edit manually. -->\n\n${generated}\n${endMarker}`;
64
64
 
65
- return content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length);
65
+ return (
66
+ content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length)
67
+ );
66
68
  }
67
69
 
68
70
  /**
@@ -91,7 +91,9 @@ function injectContentByMarker(content, markerName, generated) {
91
91
  const timestamp = new Date().toISOString().split('T')[0];
92
92
  const injectedContent = `${startMarker}\n<!-- Auto-generated on ${timestamp}. Do not edit manually. -->\n\n${generated}\n${endMarker}`;
93
93
 
94
- return content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length);
94
+ return (
95
+ content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length)
96
+ );
95
97
  }
96
98
 
97
99
  /**
@@ -117,7 +119,9 @@ function main() {
117
119
  const agents = scanAgents(agentsDir);
118
120
  const skills = scanSkills(skillsDir);
119
121
 
120
- console.log(`Found: ${commands.length} commands, ${agents.length} agents, ${skills.length} skills`);
122
+ console.log(
123
+ `Found: ${commands.length} commands, ${agents.length} agents, ${skills.length} skills`
124
+ );
121
125
 
122
126
  // Read README
123
127
  let readmeContent = fs.readFileSync(readmeFile, 'utf-8');
@@ -127,7 +131,7 @@ function main() {
127
131
  const stats = generateStats({
128
132
  commands: commands.length,
129
133
  agents: agents.length,
130
- skills: skills.length
134
+ skills: skills.length,
131
135
  });
132
136
 
133
137
  console.log('Generating agent table...');
@@ -48,10 +48,10 @@ function categorizeSkill(name, description) {
48
48
  const categories = {
49
49
  'Story & Planning': ['story', 'epic', 'sprint', 'acceptance-criteria'],
50
50
  'Code Generation': ['type-definitions', 'validation-schema', 'error-handler'],
51
- 'Testing': ['test-case'],
52
- 'Documentation': ['adr', 'api-documentation', 'changelog', 'pr-description'],
53
- 'Architecture': ['sql-schema', 'diagram'],
54
- 'Deployment': ['deployment-guide', 'migration-checklist']
51
+ Testing: ['test-case'],
52
+ Documentation: ['adr', 'api-documentation', 'changelog', 'pr-description'],
53
+ Architecture: ['sql-schema', 'diagram'],
54
+ Deployment: ['deployment-guide', 'migration-checklist'],
55
55
  };
56
56
 
57
57
  const lowerName = name.toLowerCase();
@@ -98,7 +98,7 @@ function scanSkills(skillsDir) {
98
98
  file: 'SKILL.md',
99
99
  path: skillFile,
100
100
  description,
101
- category: categorizeSkill(name, description)
101
+ category: categorizeSkill(name, description),
102
102
  });
103
103
  }
104
104
 
@@ -35,9 +35,7 @@ function getProjectInfo() {
35
35
  }
36
36
 
37
37
  try {
38
- rootPackage = JSON.parse(
39
- fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8')
40
- );
38
+ rootPackage = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));
41
39
  } catch (err) {
42
40
  // Ignore if not found
43
41
  }
@@ -50,17 +48,17 @@ function getProjectInfo() {
50
48
  try {
51
49
  gitBranch = execSync('git branch --show-current', {
52
50
  cwd: rootDir,
53
- encoding: 'utf8'
51
+ encoding: 'utf8',
54
52
  }).trim();
55
53
  gitCommit = execSync('git rev-parse --short HEAD', {
56
54
  cwd: rootDir,
57
- encoding: 'utf8'
55
+ encoding: 'utf8',
58
56
  }).trim();
59
57
 
60
58
  // Get recent commits (last 5)
61
59
  const commitLog = execSync('git log --oneline -5 2>/dev/null', {
62
60
  cwd: rootDir,
63
- encoding: 'utf8'
61
+ encoding: 'utf8',
64
62
  }).trim();
65
63
  recentCommits = commitLog.split('\n').filter(Boolean);
66
64
  } catch (err) {
@@ -68,10 +66,10 @@ function getProjectInfo() {
68
66
  }
69
67
 
70
68
  // Get AgileFlow status info
71
- let activeStories = [];
69
+ const activeStories = [];
72
70
  let wipCount = 0;
73
71
  let blockedCount = 0;
74
- let activeEpics = [];
72
+ const activeEpics = [];
75
73
 
76
74
  try {
77
75
  const statusPath = path.join(rootDir, 'docs/09-agents/status.json');
@@ -163,13 +161,16 @@ function formatOutput(info, asJson = false, compact = false) {
163
161
 
164
162
  // Header line with project info (brand color name, dim version, colored branch)
165
163
  const branchColor = info.git.branch === 'main' ? c.green : c.cyan;
166
- lines.push(`${c.brand}${c.bold}${info.project.name}${c.reset} ${c.dim}v${info.project.version}${c.reset} | ${branchColor}${info.git.branch}${c.reset} ${c.dim}(${info.git.commit})${c.reset}`);
164
+ lines.push(
165
+ `${c.brand}${c.bold}${info.project.name}${c.reset} ${c.dim}v${info.project.version}${c.reset} | ${branchColor}${info.git.branch}${c.reset} ${c.dim}(${info.git.commit})${c.reset}`
166
+ );
167
167
 
168
168
  // Status line (yellow WIP, red blocked)
169
169
  const wipColor = info.agileflow.wipCount > 0 ? c.yellow : c.dim;
170
- let statusLine = info.agileflow.wipCount > 0
171
- ? `${wipColor}WIP: ${info.agileflow.wipCount}${c.reset}`
172
- : `${c.dim}No active work${c.reset}`;
170
+ let statusLine =
171
+ info.agileflow.wipCount > 0
172
+ ? `${wipColor}WIP: ${info.agileflow.wipCount}${c.reset}`
173
+ : `${c.dim}No active work${c.reset}`;
173
174
  if (info.agileflow.blockedCount > 0) {
174
175
  statusLine += ` | ${c.red}Blocked: ${info.agileflow.blockedCount}${c.reset}`;
175
176
  }