agileflow 2.75.0 → 2.77.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.
@@ -32,6 +32,7 @@ const c = {
32
32
  bold: '\x1b[1m',
33
33
  dim: '\x1b[2m',
34
34
 
35
+ // Standard ANSI colors
35
36
  red: '\x1b[31m',
36
37
  green: '\x1b[32m',
37
38
  yellow: '\x1b[33m',
@@ -44,6 +45,22 @@ const c = {
44
45
  brightYellow: '\x1b[93m',
45
46
  brightCyan: '\x1b[96m',
46
47
 
48
+ // Vibrant 256-color palette (modern, sleek look)
49
+ mintGreen: '\x1b[38;5;158m', // Healthy/success states
50
+ peach: '\x1b[38;5;215m', // Warning states
51
+ coral: '\x1b[38;5;203m', // Critical/error states
52
+ lightGreen: '\x1b[38;5;194m', // Session healthy
53
+ lightYellow: '\x1b[38;5;228m', // Session warning
54
+ lightPink: '\x1b[38;5;210m', // Session critical
55
+ skyBlue: '\x1b[38;5;117m', // Directories/paths
56
+ lavender: '\x1b[38;5;147m', // Model info, story IDs
57
+ softGold: '\x1b[38;5;222m', // Cost/money
58
+ teal: '\x1b[38;5;80m', // Ready/pending states
59
+ slate: '\x1b[38;5;103m', // Secondary info
60
+ rose: '\x1b[38;5;211m', // Blocked/critical accent
61
+ amber: '\x1b[38;5;214m', // WIP/in-progress accent
62
+ powder: '\x1b[38;5;153m', // Labels/headers
63
+
47
64
  // Brand color (#e8683a)
48
65
  brand: '\x1b[38;2;232;104;58m',
49
66
  };
@@ -105,7 +122,9 @@ function getProjectInfo(rootDir) {
105
122
  } catch (e) {
106
123
  // Fallback: check .agileflow/package.json
107
124
  try {
108
- const pkg = JSON.parse(fs.readFileSync(path.join(rootDir, '.agileflow/package.json'), 'utf8'));
125
+ const pkg = JSON.parse(
126
+ fs.readFileSync(path.join(rootDir, '.agileflow/package.json'), 'utf8')
127
+ );
109
128
  info.version = pkg.version || info.version;
110
129
  } catch (e2) {}
111
130
  }
@@ -420,7 +439,7 @@ function getChangelogEntries(version) {
420
439
  // Run auto-update if enabled
421
440
  async function runAutoUpdate(rootDir) {
422
441
  try {
423
- console.log(`${c.cyan}Updating AgileFlow...${c.reset}`);
442
+ console.log(`${c.skyBlue}Updating AgileFlow...${c.reset}`);
424
443
  execSync('npx agileflow update', {
425
444
  cwd: rootDir,
426
445
  encoding: 'utf8',
@@ -428,11 +447,91 @@ async function runAutoUpdate(rootDir) {
428
447
  });
429
448
  return true;
430
449
  } catch (e) {
431
- console.log(`${c.yellow}Auto-update failed. Run manually: npx agileflow update${c.reset}`);
450
+ console.log(`${c.peach}Auto-update failed. Run manually: npx agileflow update${c.reset}`);
432
451
  return false;
433
452
  }
434
453
  }
435
454
 
455
+ function validateExpertise(rootDir) {
456
+ const result = { total: 0, passed: 0, warnings: 0, failed: 0, issues: [] };
457
+
458
+ // Find experts directory
459
+ let expertsDir = path.join(rootDir, '.agileflow', 'experts');
460
+ if (!fs.existsSync(expertsDir)) {
461
+ expertsDir = path.join(rootDir, 'packages', 'cli', 'src', 'core', 'experts');
462
+ }
463
+ if (!fs.existsSync(expertsDir)) {
464
+ return result; // No experts directory found
465
+ }
466
+
467
+ const STALE_DAYS = 30;
468
+ const MAX_LINES = 200;
469
+
470
+ try {
471
+ const domains = fs
472
+ .readdirSync(expertsDir, { withFileTypes: true })
473
+ .filter(d => d.isDirectory() && d.name !== 'templates')
474
+ .map(d => d.name);
475
+
476
+ for (const domain of domains) {
477
+ const filePath = path.join(expertsDir, domain, 'expertise.yaml');
478
+ if (!fs.existsSync(filePath)) {
479
+ result.total++;
480
+ result.failed++;
481
+ result.issues.push(`${domain}: missing file`);
482
+ continue;
483
+ }
484
+
485
+ result.total++;
486
+ const content = fs.readFileSync(filePath, 'utf8');
487
+ const lines = content.split('\n');
488
+ let status = 'pass';
489
+ let issue = '';
490
+
491
+ // Check required fields (use multiline flag)
492
+ const hasVersion = /^version:/m.test(content);
493
+ const hasDomain = /^domain:/m.test(content);
494
+ const hasLastUpdated = /^last_updated:/m.test(content);
495
+
496
+ if (!hasVersion || !hasDomain || !hasLastUpdated) {
497
+ status = 'fail';
498
+ issue = 'missing required fields';
499
+ }
500
+
501
+ // Check staleness
502
+ const lastUpdatedMatch = content.match(/^last_updated:\s*['"]?(\d{4}-\d{2}-\d{2})/m);
503
+ if (lastUpdatedMatch && status !== 'fail') {
504
+ const lastDate = new Date(lastUpdatedMatch[1]);
505
+ const daysSince = Math.floor((Date.now() - lastDate.getTime()) / (1000 * 60 * 60 * 24));
506
+ if (daysSince > STALE_DAYS) {
507
+ status = 'warn';
508
+ issue = `stale (${daysSince}d)`;
509
+ }
510
+ }
511
+
512
+ // Check file size
513
+ if (lines.length > MAX_LINES && status === 'pass') {
514
+ status = 'warn';
515
+ issue = `large (${lines.length} lines)`;
516
+ }
517
+
518
+ if (status === 'pass') {
519
+ result.passed++;
520
+ } else if (status === 'warn') {
521
+ result.warnings++;
522
+ result.issues.push(`${domain}: ${issue}`);
523
+ } else {
524
+ result.failed++;
525
+ result.issues.push(`${domain}: ${issue}`);
526
+ }
527
+ }
528
+ } catch (e) {
529
+ // Silently fail
530
+ }
531
+
532
+ return result;
533
+ }
534
+
436
535
  function getFeatureVersions(rootDir) {
437
536
  const result = {
438
537
  hooks: { version: null, outdated: false },
@@ -505,7 +604,15 @@ function truncate(str, maxLen, suffix = '..') {
505
604
  return str.substring(0, cutIndex) + suffix;
506
605
  }
507
606
 
508
- function formatTable(info, archival, session, precompact, parallelSessions, updateInfo = {}) {
607
+ function formatTable(
608
+ info,
609
+ archival,
610
+ session,
611
+ precompact,
612
+ parallelSessions,
613
+ updateInfo = {},
614
+ expertise = {}
615
+ ) {
509
616
  const W = 58; // inner width
510
617
  const R = W - 24; // right column width (34 chars)
511
618
  const lines = [];
@@ -526,21 +633,21 @@ function formatTable(info, archival, session, precompact, parallelSessions, upda
526
633
 
527
634
  const divider = () =>
528
635
  `${c.dim}${box.lT}${box.h.repeat(22)}${box.cross}${box.h.repeat(W - 22)}${box.rT}${c.reset}`;
529
- const fullDivider = () =>
530
- `${c.dim}${box.lT}${box.h.repeat(W)}${box.rT}${c.reset}`;
636
+ const fullDivider = () => `${c.dim}${box.lT}${box.h.repeat(W)}${box.rT}${c.reset}`;
531
637
  const topBorder = `${c.dim}${box.tl}${box.h.repeat(22)}${box.tT}${box.h.repeat(W - 22)}${box.tr}${c.reset}`;
532
638
  const bottomBorder = `${c.dim}${box.bl}${box.h.repeat(22)}${box.bT}${box.h.repeat(W - 22)}${box.br}${c.reset}`;
533
639
 
534
640
  // Header with version and optional update indicator
641
+ // Use vibrant colors for branch
535
642
  const branchColor =
536
- info.branch === 'main' ? c.green : info.branch.startsWith('fix') ? c.red : c.cyan;
643
+ info.branch === 'main' ? c.mintGreen : info.branch.startsWith('fix') ? c.coral : c.skyBlue;
537
644
 
538
- // Build version string with update status
645
+ // Build version string with update status (vibrant colors)
539
646
  let versionStr = `v${info.version}`;
540
647
  if (updateInfo.justUpdated && updateInfo.previousVersion) {
541
- versionStr = `v${info.version} ${c.green}✓${c.reset}${c.dim} (was v${updateInfo.previousVersion})`;
648
+ versionStr = `v${info.version} ${c.mintGreen}✓${c.reset}${c.slate} (was v${updateInfo.previousVersion})`;
542
649
  } else if (updateInfo.available && updateInfo.latest) {
543
- versionStr = `v${info.version} ${c.yellow}↑${updateInfo.latest}${c.reset}`;
650
+ versionStr = `v${info.version} ${c.amber}↑${updateInfo.latest}${c.reset}`;
544
651
  }
545
652
 
546
653
  // Calculate remaining space for branch
@@ -561,125 +668,139 @@ function formatTable(info, archival, session, precompact, parallelSessions, upda
561
668
  lines.push(topBorder);
562
669
  lines.push(headerLine);
563
670
 
564
- // Show update available notification
671
+ // Show update available notification (using vibrant colors)
565
672
  if (updateInfo.available && updateInfo.latest && !updateInfo.justUpdated) {
566
673
  lines.push(fullDivider());
567
- lines.push(fullRow(`↑ Update available: v${updateInfo.latest}`, c.yellow));
568
- lines.push(fullRow(` Run: npx agileflow update`, c.dim));
674
+ lines.push(fullRow(`${c.amber}↑${c.reset} Update available: ${c.softGold}v${updateInfo.latest}${c.reset}`, ''));
675
+ lines.push(fullRow(` Run: ${c.skyBlue}npx agileflow update${c.reset}`, ''));
569
676
  }
570
677
 
571
678
  // Show "just updated" changelog
572
679
  if (updateInfo.justUpdated && updateInfo.changelog && updateInfo.changelog.length > 0) {
573
680
  lines.push(fullDivider());
574
- lines.push(fullRow(`What's new in v${info.version}:`, c.green));
681
+ lines.push(fullRow(`${c.mintGreen}✨${c.reset} What's new in ${c.softGold}v${info.version}${c.reset}:`, ''));
575
682
  for (const entry of updateInfo.changelog.slice(0, 2)) {
576
- lines.push(fullRow(`• ${truncate(entry, W - 4)}`, c.dim));
683
+ lines.push(fullRow(` ${c.teal}•${c.reset} ${truncate(entry, W - 6)}`, ''));
577
684
  }
578
- lines.push(fullRow(`Run /agileflow:whats-new for full changelog`, c.dim));
685
+ lines.push(fullRow(` Run ${c.skyBlue}/agileflow:whats-new${c.reset} for full changelog`, ''));
579
686
  }
580
687
 
581
688
  lines.push(divider());
582
689
 
583
- // Stories section
690
+ // Stories section (always colorful labels like obtain-context)
584
691
  lines.push(
585
692
  row(
586
693
  'In Progress',
587
694
  info.wipCount > 0 ? `${info.wipCount}` : '0',
588
- c.dim,
589
- info.wipCount > 0 ? c.yellow : c.dim
695
+ c.peach,
696
+ info.wipCount > 0 ? c.peach : c.dim
590
697
  )
591
698
  );
592
699
  lines.push(
593
700
  row(
594
701
  'Blocked',
595
702
  info.blockedCount > 0 ? `${info.blockedCount}` : '0',
596
- c.dim,
597
- info.blockedCount > 0 ? c.red : c.dim
703
+ c.coral,
704
+ info.blockedCount > 0 ? c.coral : c.dim
598
705
  )
599
706
  );
600
707
  lines.push(
601
708
  row(
602
709
  'Ready',
603
710
  info.readyCount > 0 ? `${info.readyCount}` : '0',
604
- c.dim,
605
- info.readyCount > 0 ? c.cyan : c.dim
711
+ c.skyBlue,
712
+ info.readyCount > 0 ? c.skyBlue : c.dim
606
713
  )
607
714
  );
715
+ const completedColor = `${c.bold}${c.mintGreen}`;
608
716
  lines.push(
609
717
  row(
610
718
  'Completed',
611
719
  info.completedCount > 0 ? `${info.completedCount}` : '0',
612
- c.dim,
613
- info.completedCount > 0 ? c.green : c.dim
720
+ completedColor,
721
+ info.completedCount > 0 ? completedColor : c.dim
614
722
  )
615
723
  );
616
724
 
617
725
  lines.push(divider());
618
726
 
619
- // Archival section
727
+ // System section (colorful labels like obtain-context)
620
728
  if (archival.disabled) {
621
- lines.push(row('Auto-archival', 'disabled', c.dim, c.dim));
729
+ lines.push(row('Auto-archival', 'disabled', c.lavender, c.slate));
622
730
  } else {
623
731
  const archivalStatus =
624
732
  archival.archived > 0 ? `archived ${archival.archived} stories` : `nothing to archive`;
625
733
  lines.push(
626
- row('Auto-archival', archivalStatus, c.dim, archival.archived > 0 ? c.green : c.dim)
734
+ row('Auto-archival', archivalStatus, c.lavender, archival.archived > 0 ? c.mintGreen : c.dim)
627
735
  );
628
736
  }
629
737
 
630
738
  // Session cleanup
631
739
  const sessionStatus = session.cleared > 0 ? `cleared ${session.cleared} command(s)` : `clean`;
632
- lines.push(row('Session state', sessionStatus, c.dim, session.cleared > 0 ? c.green : c.dim));
740
+ lines.push(row('Session state', sessionStatus, c.lavender, session.cleared > 0 ? c.mintGreen : c.dim));
633
741
 
634
742
  // PreCompact status with version check
635
743
  if (precompact.configured && precompact.scriptExists) {
636
744
  if (precompact.outdated) {
637
745
  const verStr = precompact.version ? ` (v${precompact.version})` : '';
638
- lines.push(row('Context preserve', `outdated${verStr}`, c.dim, c.yellow));
746
+ lines.push(row('Context preserve', `outdated${verStr}`, c.peach, c.peach));
639
747
  } else if (session.commandNames && session.commandNames.length > 0) {
640
748
  // Show the preserved command names
641
749
  const cmdDisplay = session.commandNames.map(n => `/agileflow:${n}`).join(', ');
642
- lines.push(row('Context preserve', cmdDisplay, c.dim, c.green));
750
+ lines.push(row('Context preserve', cmdDisplay, c.lavender, c.mintGreen));
643
751
  } else {
644
- lines.push(row('Context preserve', 'nothing to compact', c.dim, c.dim));
752
+ lines.push(row('Context preserve', 'ready', c.lavender, c.dim));
645
753
  }
646
754
  } else if (precompact.configured) {
647
- lines.push(row('Context preserve', 'script missing', c.dim, c.yellow));
755
+ lines.push(row('Context preserve', 'script missing', c.peach, c.peach));
648
756
  } else {
649
- lines.push(row('Context preserve', 'not configured', c.dim, c.dim));
757
+ lines.push(row('Context preserve', 'not configured', c.slate, c.slate));
650
758
  }
651
759
 
652
760
  // Parallel sessions status
653
761
  if (parallelSessions && parallelSessions.available) {
654
762
  if (parallelSessions.otherActive > 0) {
655
763
  const sessionStr = `⚠️ ${parallelSessions.otherActive} other active`;
656
- lines.push(row('Sessions', sessionStr, c.dim, c.yellow));
764
+ lines.push(row('Sessions', sessionStr, c.peach, c.peach));
657
765
  } else {
658
766
  const sessionStr = parallelSessions.currentId
659
767
  ? `✓ Session ${parallelSessions.currentId} (only)`
660
768
  : '✓ Only session';
661
- lines.push(row('Sessions', sessionStr, c.dim, c.green));
769
+ lines.push(row('Sessions', sessionStr, c.lavender, c.mintGreen));
770
+ }
771
+ }
772
+
773
+ // Agent expertise validation (always show with color)
774
+ if (expertise && expertise.total > 0) {
775
+ if (expertise.failed > 0) {
776
+ const expertStr = `❌ ${expertise.failed} failed, ${expertise.warnings} warnings`;
777
+ lines.push(row('Expertise', expertStr, c.coral, c.coral));
778
+ } else if (expertise.warnings > 0) {
779
+ const expertStr = `⚠️ ${expertise.warnings} warnings (${expertise.passed} ok)`;
780
+ lines.push(row('Expertise', expertStr, c.peach, c.peach));
781
+ } else {
782
+ lines.push(row('Expertise', `✓ ${expertise.total} valid`, c.lavender, c.mintGreen));
662
783
  }
663
784
  }
664
785
 
665
786
  lines.push(divider());
666
787
 
667
- // Current story (if any) - row() auto-truncates
788
+ // Current story (colorful like obtain-context)
668
789
  if (info.currentStory) {
669
790
  lines.push(
670
791
  row(
671
792
  'Current',
672
- `${c.blue}${info.currentStory.id}${c.reset}: ${info.currentStory.title}`,
673
- c.dim,
793
+ `${c.lightYellow}${info.currentStory.id}${c.reset}: ${info.currentStory.title}`,
794
+ c.skyBlue,
674
795
  ''
675
796
  )
676
797
  );
677
798
  } else {
678
- lines.push(row('Current', 'No active story', c.dim, c.dim));
799
+ lines.push(row('Current', 'No active story', c.skyBlue, c.dim));
679
800
  }
680
801
 
681
- // Last commit - row() auto-truncates
682
- lines.push(row('Last commit', `${info.commit} ${info.lastCommit}`, c.dim, c.dim));
802
+ // Last commit (colorful like obtain-context)
803
+ lines.push(row('Last commit', `${c.peach}${info.commit}${c.reset} ${info.lastCommit}`, c.lavender, ''));
683
804
 
684
805
  lines.push(bottomBorder);
685
806
 
@@ -694,6 +815,7 @@ async function main() {
694
815
  const session = clearActiveCommands(rootDir);
695
816
  const precompact = checkPreCompact(rootDir);
696
817
  const parallelSessions = checkParallelSessions(rootDir);
818
+ const expertise = validateExpertise(rootDir);
697
819
 
698
820
  // Check for updates (async, cached)
699
821
  let updateInfo = {};
@@ -717,14 +839,16 @@ async function main() {
717
839
  // Update check failed - continue without it
718
840
  }
719
841
 
720
- console.log(formatTable(info, archival, session, precompact, parallelSessions, updateInfo));
842
+ console.log(
843
+ formatTable(info, archival, session, precompact, parallelSessions, updateInfo, expertise)
844
+ );
721
845
 
722
- // Show warning and tip if other sessions are active
846
+ // Show warning and tip if other sessions are active (vibrant colors)
723
847
  if (parallelSessions.otherActive > 0) {
724
848
  console.log('');
725
- console.log(`${c.yellow}⚠️ Other Claude session(s) active in this repo.${c.reset}`);
726
- console.log(`${c.dim} Run /agileflow:session:status to see all sessions.${c.reset}`);
727
- console.log(`${c.dim} Run /agileflow:session:new to create isolated workspace.${c.reset}`);
849
+ console.log(`${c.amber}⚠️ Other Claude session(s) active in this repo.${c.reset}`);
850
+ console.log(`${c.slate} Run ${c.skyBlue}/agileflow:session:status${c.reset}${c.slate} to see all sessions.${c.reset}`);
851
+ console.log(`${c.slate} Run ${c.skyBlue}/agileflow:session:new${c.reset}${c.slate} to create isolated workspace.${c.reset}`);
728
852
  }
729
853
  }
730
854