cmp-standards 3.1.1 → 3.3.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 (80) hide show
  1. package/dist/cli/index.js +488 -1
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/db/migrations.d.ts +1 -1
  4. package/dist/db/migrations.d.ts.map +1 -1
  5. package/dist/db/migrations.js +102 -2
  6. package/dist/db/migrations.js.map +1 -1
  7. package/dist/eslint/ast-types.d.ts +235 -0
  8. package/dist/eslint/ast-types.d.ts.map +1 -0
  9. package/dist/eslint/ast-types.js +9 -0
  10. package/dist/eslint/ast-types.js.map +1 -0
  11. package/dist/eslint/rules/consistent-error-handling.d.ts.map +1 -1
  12. package/dist/eslint/rules/consistent-error-handling.js +2 -1
  13. package/dist/eslint/rules/consistent-error-handling.js.map +1 -1
  14. package/dist/eslint/rules/no-async-useeffect.js.map +1 -1
  15. package/dist/events/EventBus.js.map +1 -1
  16. package/dist/events/types.d.ts +174 -4
  17. package/dist/events/types.d.ts.map +1 -1
  18. package/dist/events/types.js +15 -0
  19. package/dist/events/types.js.map +1 -1
  20. package/dist/hooks/session-start.js +3 -3
  21. package/dist/hooks/session-start.js.map +1 -1
  22. package/dist/index.d.ts +11 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +21 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/mcp/server.d.ts.map +1 -1
  27. package/dist/mcp/server.js +8 -4
  28. package/dist/mcp/server.js.map +1 -1
  29. package/dist/patterns/feedback-loop.d.ts +2 -2
  30. package/dist/patterns/lifecycle.d.ts.map +1 -1
  31. package/dist/patterns/lifecycle.js +11 -13
  32. package/dist/patterns/lifecycle.js.map +1 -1
  33. package/dist/patterns/registry.d.ts +2 -2
  34. package/dist/plugins/PluginManager.d.ts.map +1 -1
  35. package/dist/plugins/PluginManager.js +4 -1
  36. package/dist/plugins/PluginManager.js.map +1 -1
  37. package/dist/schema/codewiki-types.d.ts +1899 -0
  38. package/dist/schema/codewiki-types.d.ts.map +1 -0
  39. package/dist/schema/codewiki-types.js +585 -0
  40. package/dist/schema/codewiki-types.js.map +1 -0
  41. package/dist/schema/expert-types.d.ts +2 -2
  42. package/dist/schema/opportunity-types.d.ts +505 -0
  43. package/dist/schema/opportunity-types.d.ts.map +1 -0
  44. package/dist/schema/opportunity-types.js +255 -0
  45. package/dist/schema/opportunity-types.js.map +1 -0
  46. package/dist/services/AuditLog.d.ts.map +1 -1
  47. package/dist/services/AuditLog.js +4 -1
  48. package/dist/services/AuditLog.js.map +1 -1
  49. package/dist/services/CodeWikiIndexer.d.ts +145 -0
  50. package/dist/services/CodeWikiIndexer.d.ts.map +1 -0
  51. package/dist/services/CodeWikiIndexer.js +664 -0
  52. package/dist/services/CodeWikiIndexer.js.map +1 -0
  53. package/dist/services/HookVerifier.d.ts +8 -3
  54. package/dist/services/HookVerifier.d.ts.map +1 -1
  55. package/dist/services/HookVerifier.js +29 -22
  56. package/dist/services/HookVerifier.js.map +1 -1
  57. package/dist/services/OpportunityDiscovery.d.ts +84 -0
  58. package/dist/services/OpportunityDiscovery.d.ts.map +1 -0
  59. package/dist/services/OpportunityDiscovery.js +754 -0
  60. package/dist/services/OpportunityDiscovery.js.map +1 -0
  61. package/dist/services/ProjectScanner.d.ts.map +1 -1
  62. package/dist/services/ProjectScanner.js +1 -1
  63. package/dist/services/ProjectScanner.js.map +1 -1
  64. package/dist/services/index.d.ts +1 -0
  65. package/dist/services/index.d.ts.map +1 -1
  66. package/dist/services/index.js +2 -0
  67. package/dist/services/index.js.map +1 -1
  68. package/dist/utils/env.d.ts +149 -0
  69. package/dist/utils/env.d.ts.map +1 -0
  70. package/dist/utils/env.js +223 -0
  71. package/dist/utils/env.js.map +1 -0
  72. package/dist/utils/index.d.ts +3 -0
  73. package/dist/utils/index.d.ts.map +1 -1
  74. package/dist/utils/index.js +6 -0
  75. package/dist/utils/index.js.map +1 -1
  76. package/dist/utils/logger.d.ts +126 -0
  77. package/dist/utils/logger.d.ts.map +1 -0
  78. package/dist/utils/logger.js +231 -0
  79. package/dist/utils/logger.js.map +1 -0
  80. package/package.json +1 -1
package/dist/cli/index.js CHANGED
@@ -1396,6 +1396,493 @@ program
1396
1396
  }
1397
1397
  });
1398
1398
  // =============================================================================
1399
+ // OPPORTUNITIES COMMAND
1400
+ // =============================================================================
1401
+ program
1402
+ .command('opportunities')
1403
+ .description('Discover improvement opportunities across the codebase')
1404
+ .argument('[paths...]', 'Files or directories to analyze')
1405
+ .option('-c, --category <category>', 'Filter by category (idea, edge_case, security, performance, etc.)')
1406
+ .option('-i, --impact <impact>', 'Minimum impact level (low, medium, high)', 'low')
1407
+ .option('-e, --effort <effort>', 'Maximum effort level (trivial, small, medium, large, epic)', 'epic')
1408
+ .option('-d, --depth <depth>', 'Analysis depth (quick, standard, thorough)', 'standard')
1409
+ .option('--export', 'Export results as markdown')
1410
+ .option('--json', 'Output results as JSON')
1411
+ .action(async (paths, options) => {
1412
+ ui.header('Opportunity Discovery');
1413
+ const config = await loadConfig(process.cwd());
1414
+ const system = config.system;
1415
+ const { getOpportunityDiscovery } = await import('../services/OpportunityDiscovery.js');
1416
+ const discovery = getOpportunityDiscovery(system);
1417
+ // Parse category filter
1418
+ const categories = options.category
1419
+ ? [options.category]
1420
+ : [];
1421
+ const result = await withSpinner(`Analyzing codebase (${options.depth} mode)...`, () => discovery.discover({
1422
+ paths: paths.length > 0 ? paths : ['.'],
1423
+ categories,
1424
+ minImpact: options.impact,
1425
+ maxEffort: options.effort,
1426
+ depth: options.depth,
1427
+ system,
1428
+ }));
1429
+ if (options.json) {
1430
+ console.log(JSON.stringify(result, null, 2));
1431
+ return;
1432
+ }
1433
+ if (options.export) {
1434
+ const markdown = await discovery.exportMarkdown(result.opportunities);
1435
+ console.log(markdown);
1436
+ return;
1437
+ }
1438
+ // Display summary
1439
+ console.log('');
1440
+ ui.subheader('Summary');
1441
+ console.log(` Files analyzed: ${chalk.cyan(result.summary.filesAnalyzed)}`);
1442
+ console.log(` Opportunities found: ${chalk.cyan(result.summary.totalFound)}`);
1443
+ console.log(` Analysis time: ${chalk.gray(result.summary.analysisTimeMs + 'ms')}`);
1444
+ console.log('');
1445
+ // Display by category
1446
+ ui.subheader('By Category');
1447
+ const categoryNames = {
1448
+ idea: '💡 Ideas',
1449
+ edge_case: '⚠️ Edge Cases',
1450
+ future_feature: '🚀 Future Features',
1451
+ technical_debt: '🔧 Technical Debt',
1452
+ performance: '⚡ Performance',
1453
+ security: '🔒 Security',
1454
+ dx_improvement: '👩‍💻 DX Improvements',
1455
+ documentation: '📚 Documentation',
1456
+ test_coverage: '🧪 Test Coverage',
1457
+ accessibility: '♿ Accessibility',
1458
+ };
1459
+ for (const [category, count] of Object.entries(result.summary.byCategory)) {
1460
+ const name = categoryNames[category] ?? category;
1461
+ console.log(` ${name}: ${chalk.yellow(count)}`);
1462
+ }
1463
+ console.log('');
1464
+ // Display by impact
1465
+ ui.subheader('By Impact');
1466
+ const impactColors = {
1467
+ high: chalk.red,
1468
+ medium: chalk.yellow,
1469
+ low: chalk.gray,
1470
+ };
1471
+ for (const [impact, count] of Object.entries(result.summary.byImpact)) {
1472
+ const color = impactColors[impact] ?? chalk.white;
1473
+ console.log(` ${impact.toUpperCase()}: ${color(count.toString())}`);
1474
+ }
1475
+ console.log('');
1476
+ // Display top opportunities
1477
+ if (result.opportunities.length > 0) {
1478
+ ui.subheader('Top Opportunities');
1479
+ // Sort by impact (high first)
1480
+ const impactOrder = ['high', 'medium', 'low'];
1481
+ const sorted = [...result.opportunities].sort((a, b) => {
1482
+ return impactOrder.indexOf(a.impact) - impactOrder.indexOf(b.impact);
1483
+ });
1484
+ const top = sorted.slice(0, 10);
1485
+ for (const opp of top) {
1486
+ const icon = categoryNames[opp.category]?.slice(0, 2) ?? '📋';
1487
+ const impactColor = impactColors[opp.impact] ?? chalk.white;
1488
+ console.log(` ${icon} ${chalk.white(opp.title)}`);
1489
+ console.log(` Impact: ${impactColor(opp.impact)} | Effort: ${chalk.gray(opp.effort)} | Confidence: ${chalk.gray(Math.round(opp.confidence * 100) + '%')}`);
1490
+ if (opp.relatedFiles.length > 0) {
1491
+ console.log(` File: ${chalk.gray(opp.relatedFiles[0])}`);
1492
+ }
1493
+ console.log('');
1494
+ }
1495
+ if (result.opportunities.length > 10) {
1496
+ console.log(chalk.gray(` ... and ${result.opportunities.length - 10} more opportunities`));
1497
+ console.log(chalk.gray(' Run with --export to see all'));
1498
+ }
1499
+ }
1500
+ // Display top files with issues
1501
+ if (result.summary.topFiles.length > 0) {
1502
+ console.log('');
1503
+ ui.subheader('Files with Most Opportunities');
1504
+ for (const { file, count } of result.summary.topFiles.slice(0, 5)) {
1505
+ console.log(` ${chalk.gray(file)}: ${chalk.yellow(count)}`);
1506
+ }
1507
+ }
1508
+ console.log('');
1509
+ ui.success(`Run ID: ${result.runId}`);
1510
+ });
1511
+ // =============================================================================
1512
+ // WIKI COMMAND (CodeWiki - Code Knowledge Graph)
1513
+ // =============================================================================
1514
+ const wikiCmd = program
1515
+ .command('wiki')
1516
+ .description('CodeWiki - Code knowledge graph and documentation');
1517
+ wikiCmd
1518
+ .command('index')
1519
+ .description('Index codebase for CodeWiki knowledge graph')
1520
+ .argument('[paths...]', 'Paths to index (defaults to current directory)')
1521
+ .option('-d, --depth <depth>', 'Index depth (quick, standard, thorough)', 'standard')
1522
+ .option('-f, --force', 'Force re-index all files (ignore cache)')
1523
+ .option('-s, --system <system>', 'System identifier')
1524
+ .option('--json', 'Output as JSON')
1525
+ .action(async (paths, options) => {
1526
+ ui.header('CodeWiki Indexer');
1527
+ const projectRoot = await getProjectRoot();
1528
+ let system = options.system;
1529
+ if (!system) {
1530
+ try {
1531
+ const config = await loadConfig(projectRoot);
1532
+ system = config.system;
1533
+ }
1534
+ catch {
1535
+ system = 'UNKNOWN';
1536
+ }
1537
+ }
1538
+ const { getCodeWikiIndexer } = await import('../services/CodeWikiIndexer.js');
1539
+ const indexer = getCodeWikiIndexer(system, projectRoot);
1540
+ const result = await withSpinner(`Indexing codebase (${options.depth} mode)...`, () => indexer.index({
1541
+ projectId: system,
1542
+ paths: paths.length > 0 ? paths : ['.'],
1543
+ rootDir: projectRoot,
1544
+ depth: options.depth,
1545
+ force: options.force ?? false,
1546
+ }));
1547
+ if (options.json) {
1548
+ console.log(JSON.stringify(result, null, 2));
1549
+ return;
1550
+ }
1551
+ console.log('');
1552
+ ui.subheader('Indexing Complete');
1553
+ console.log(` Run ID: ${chalk.cyan(result.runId)}`);
1554
+ console.log(` Status: ${result.status === 'completed' ? chalk.green('✓ Completed') : chalk.red(result.status)}`);
1555
+ console.log(` Duration: ${chalk.gray(result.durationMs + 'ms')}`);
1556
+ console.log('');
1557
+ ui.subheader('Statistics');
1558
+ ui.keyValue({
1559
+ 'Files Scanned': result.stats.filesScanned,
1560
+ 'Files Indexed': result.stats.filesIndexed,
1561
+ 'Files Skipped (unchanged)': result.stats.filesSkipped,
1562
+ 'Files Changed': result.stats.filesChanged,
1563
+ 'Symbols Extracted': result.stats.symbolsExtracted,
1564
+ 'Dependencies Found': result.stats.dependenciesFound,
1565
+ 'Errors': result.stats.errorsEncountered,
1566
+ });
1567
+ if (result.errors.length > 0) {
1568
+ console.log('');
1569
+ ui.subheader('Errors');
1570
+ for (const error of result.errors.slice(0, 5)) {
1571
+ console.log(` ❌ ${chalk.gray(error.file)}: ${error.error}`);
1572
+ }
1573
+ if (result.errors.length > 5) {
1574
+ console.log(chalk.gray(` ... and ${result.errors.length - 5} more errors`));
1575
+ }
1576
+ }
1577
+ });
1578
+ wikiCmd
1579
+ .command('status')
1580
+ .description('Show CodeWiki index status')
1581
+ .option('-s, --system <system>', 'System identifier')
1582
+ .action(async (options) => {
1583
+ ui.header('CodeWiki Status');
1584
+ const projectRoot = await getProjectRoot();
1585
+ let system = options.system;
1586
+ if (!system) {
1587
+ try {
1588
+ const config = await loadConfig(projectRoot);
1589
+ system = config.system;
1590
+ }
1591
+ catch {
1592
+ system = 'UNKNOWN';
1593
+ }
1594
+ }
1595
+ const { query: tursoQuery } = await import('../db/turso-client.js');
1596
+ // Get latest index run
1597
+ const runs = await tursoQuery({
1598
+ type: 'index_run',
1599
+ system,
1600
+ limit: 5,
1601
+ });
1602
+ if (runs.length === 0) {
1603
+ console.log(chalk.yellow(' No index runs found'));
1604
+ console.log(chalk.gray(' Run: cmp-standards wiki index'));
1605
+ return;
1606
+ }
1607
+ ui.subheader('Recent Index Runs');
1608
+ for (const run of runs) {
1609
+ try {
1610
+ const content = run.content;
1611
+ const status = content.status === 'completed' ? chalk.green('✓') : chalk.yellow(content.status);
1612
+ const files = content.stats?.filesIndexed ?? 0;
1613
+ const symbols = content.stats?.symbolsExtracted ?? 0;
1614
+ const duration = content.durationMs ? `${content.durationMs}ms` : 'running';
1615
+ console.log(` ${status} ${chalk.gray(content.startedAt)} - ${files} files, ${symbols} symbols (${duration})`);
1616
+ }
1617
+ catch {
1618
+ // Skip malformed records
1619
+ }
1620
+ }
1621
+ // Get counts
1622
+ console.log('');
1623
+ ui.subheader('Index Summary');
1624
+ const fileMetadata = await tursoQuery({ type: 'file_metadata', system, limit: 1 });
1625
+ const codeStructure = await tursoQuery({ type: 'code_structure', system, limit: 1 });
1626
+ const codeDependency = await tursoQuery({ type: 'code_dependency', system, limit: 1 });
1627
+ ui.keyValue({
1628
+ 'File Metadata': fileMetadata.length > 0 ? '✓' : '✗',
1629
+ 'Code Structure': codeStructure.length > 0 ? '✓' : '✗',
1630
+ 'Dependencies': codeDependency.length > 0 ? '✓' : '✗',
1631
+ });
1632
+ });
1633
+ wikiCmd
1634
+ .command('search <query>')
1635
+ .description('Search CodeWiki for symbols, files, or patterns')
1636
+ .option('-t, --type <type>', 'Filter by type (function, class, interface, type, component)')
1637
+ .option('-f, --file <file>', 'Filter by file pattern')
1638
+ .option('-s, --system <system>', 'System identifier')
1639
+ .option('-l, --limit <limit>', 'Maximum results', '20')
1640
+ .action(async (query, options) => {
1641
+ ui.header('CodeWiki Search');
1642
+ const projectRoot = await getProjectRoot();
1643
+ let system = options.system;
1644
+ if (!system) {
1645
+ try {
1646
+ const config = await loadConfig(projectRoot);
1647
+ system = config.system;
1648
+ }
1649
+ catch {
1650
+ system = 'UNKNOWN';
1651
+ }
1652
+ }
1653
+ const { query: tursoQuery } = await import('../db/turso-client.js');
1654
+ // Search code structures for symbols
1655
+ const structures = await tursoQuery({
1656
+ type: 'code_structure',
1657
+ system,
1658
+ limit: 100,
1659
+ });
1660
+ const results = [];
1661
+ const queryLower = query.toLowerCase();
1662
+ const limit = parseInt(options.limit, 10);
1663
+ for (const struct of structures) {
1664
+ try {
1665
+ const content = struct.content;
1666
+ const symbols = content.symbols ?? [];
1667
+ for (const symbol of symbols) {
1668
+ // Check name match
1669
+ if (!symbol.name.toLowerCase().includes(queryLower))
1670
+ continue;
1671
+ // Check type filter
1672
+ if (options.type && symbol.kind !== options.type)
1673
+ continue;
1674
+ // Check file filter
1675
+ if (options.file && !content.filePath.includes(options.file))
1676
+ continue;
1677
+ results.push({
1678
+ name: symbol.name,
1679
+ kind: symbol.kind,
1680
+ file: content.filePath,
1681
+ line: symbol.lineStart,
1682
+ exported: symbol.isExported,
1683
+ });
1684
+ if (results.length >= limit)
1685
+ break;
1686
+ }
1687
+ }
1688
+ catch {
1689
+ // Skip malformed records
1690
+ }
1691
+ if (results.length >= limit)
1692
+ break;
1693
+ }
1694
+ if (results.length === 0) {
1695
+ console.log(chalk.yellow(` No results found for "${query}"`));
1696
+ return;
1697
+ }
1698
+ console.log(` Found ${chalk.cyan(results.length)} results:\n`);
1699
+ const kindIcons = {
1700
+ function: 'ƒ',
1701
+ arrow_function: '→',
1702
+ class: '◆',
1703
+ interface: '◇',
1704
+ type: 'τ',
1705
+ enum: '∈',
1706
+ const: '●',
1707
+ component: '⬡',
1708
+ hook: '⚓',
1709
+ };
1710
+ for (const result of results) {
1711
+ const icon = kindIcons[result.kind] ?? '○';
1712
+ const exported = result.exported ? chalk.green('exported') : chalk.gray('internal');
1713
+ console.log(` ${icon} ${chalk.white(result.name)} ${chalk.gray(`(${result.kind})`)} [${exported}]`);
1714
+ console.log(` ${chalk.gray(result.file)}:${result.line}`);
1715
+ }
1716
+ });
1717
+ wikiCmd
1718
+ .command('deps <file>')
1719
+ .description('Show dependencies for a file')
1720
+ .option('-d, --direction <dir>', 'Direction: incoming, outgoing, both', 'both')
1721
+ .option('-s, --system <system>', 'System identifier')
1722
+ .action(async (file, options) => {
1723
+ ui.header(`Dependencies: ${file}`);
1724
+ const projectRoot = await getProjectRoot();
1725
+ let system = options.system;
1726
+ if (!system) {
1727
+ try {
1728
+ const config = await loadConfig(projectRoot);
1729
+ system = config.system;
1730
+ }
1731
+ catch {
1732
+ system = 'UNKNOWN';
1733
+ }
1734
+ }
1735
+ const { query: tursoQuery } = await import('../db/turso-client.js');
1736
+ const deps = await tursoQuery({
1737
+ type: 'code_dependency',
1738
+ system,
1739
+ limit: 500,
1740
+ });
1741
+ const incoming = [];
1742
+ const outgoing = [];
1743
+ for (const dep of deps) {
1744
+ try {
1745
+ const content = dep.content;
1746
+ if (content.sourceFile.includes(file)) {
1747
+ outgoing.push({
1748
+ to: content.targetFile,
1749
+ symbols: content.symbols ?? [],
1750
+ external: content.isExternal,
1751
+ });
1752
+ }
1753
+ if (content.targetFile.includes(file)) {
1754
+ incoming.push({
1755
+ from: content.sourceFile,
1756
+ symbols: content.symbols ?? [],
1757
+ });
1758
+ }
1759
+ }
1760
+ catch {
1761
+ // Skip malformed records
1762
+ }
1763
+ }
1764
+ if (options.direction !== 'outgoing' && incoming.length > 0) {
1765
+ ui.subheader(`Incoming Dependencies (${incoming.length})`);
1766
+ for (const dep of incoming.slice(0, 20)) {
1767
+ console.log(` ← ${chalk.gray(dep.from)}`);
1768
+ if (dep.symbols.length > 0) {
1769
+ console.log(` imports: ${chalk.cyan(dep.symbols.join(', '))}`);
1770
+ }
1771
+ }
1772
+ if (incoming.length > 20) {
1773
+ console.log(chalk.gray(` ... and ${incoming.length - 20} more`));
1774
+ }
1775
+ }
1776
+ if (options.direction !== 'incoming' && outgoing.length > 0) {
1777
+ console.log('');
1778
+ ui.subheader(`Outgoing Dependencies (${outgoing.length})`);
1779
+ for (const dep of outgoing.slice(0, 20)) {
1780
+ const icon = dep.external ? chalk.yellow('📦') : '→';
1781
+ console.log(` ${icon} ${chalk.gray(dep.to)}`);
1782
+ if (dep.symbols.length > 0) {
1783
+ console.log(` uses: ${chalk.cyan(dep.symbols.join(', '))}`);
1784
+ }
1785
+ }
1786
+ if (outgoing.length > 20) {
1787
+ console.log(chalk.gray(` ... and ${outgoing.length - 20} more`));
1788
+ }
1789
+ }
1790
+ if (incoming.length === 0 && outgoing.length === 0) {
1791
+ console.log(chalk.yellow(' No dependencies found for this file'));
1792
+ console.log(chalk.gray(' Run: cmp-standards wiki index'));
1793
+ }
1794
+ });
1795
+ wikiCmd
1796
+ .command('symbols <file>')
1797
+ .description('List all symbols in a file')
1798
+ .option('-s, --system <system>', 'System identifier')
1799
+ .action(async (file, options) => {
1800
+ ui.header(`Symbols: ${file}`);
1801
+ const projectRoot = await getProjectRoot();
1802
+ let system = options.system;
1803
+ if (!system) {
1804
+ try {
1805
+ const config = await loadConfig(projectRoot);
1806
+ system = config.system;
1807
+ }
1808
+ catch {
1809
+ system = 'UNKNOWN';
1810
+ }
1811
+ }
1812
+ const { query: tursoQuery } = await import('../db/turso-client.js');
1813
+ const structures = await tursoQuery({
1814
+ type: 'code_structure',
1815
+ system,
1816
+ limit: 100,
1817
+ });
1818
+ for (const struct of structures) {
1819
+ try {
1820
+ const content = struct.content;
1821
+ if (!content.filePath.includes(file))
1822
+ continue;
1823
+ // Found the file
1824
+ console.log(` File: ${chalk.cyan(content.filePath)}`);
1825
+ console.log(` Framework: ${content.framework ?? 'none'}`);
1826
+ console.log(` Component: ${content.isComponent ? 'Yes' : 'No'}`);
1827
+ console.log(` Test: ${content.isTest ? 'Yes' : 'No'}`);
1828
+ console.log('');
1829
+ // Imports
1830
+ const imports = content.imports ?? [];
1831
+ if (imports.length > 0) {
1832
+ ui.subheader(`Imports (${imports.length})`);
1833
+ for (const imp of imports) {
1834
+ const icon = imp.isExternal ? chalk.yellow('📦') : '📁';
1835
+ const names = imp.names?.map((n) => n.alias ? `${n.name} as ${n.alias}` : n.name).join(', ') ?? '';
1836
+ console.log(` ${icon} ${chalk.gray(imp.source)}`);
1837
+ if (names) {
1838
+ console.log(` ${chalk.cyan(names)}`);
1839
+ }
1840
+ }
1841
+ console.log('');
1842
+ }
1843
+ // Exports
1844
+ const exports = content.exports ?? [];
1845
+ if (exports.length > 0) {
1846
+ ui.subheader(`Exports (${exports.length})`);
1847
+ for (const exp of exports) {
1848
+ const defaultMark = exp.isDefault ? chalk.green('default') : '';
1849
+ console.log(` ↗ ${chalk.white(exp.name)} ${defaultMark}`);
1850
+ }
1851
+ console.log('');
1852
+ }
1853
+ // Symbols
1854
+ const symbols = content.symbols ?? [];
1855
+ if (symbols.length > 0) {
1856
+ ui.subheader(`Symbols (${symbols.length})`);
1857
+ const kindIcons = {
1858
+ function: 'ƒ',
1859
+ arrow_function: '→',
1860
+ class: '◆',
1861
+ interface: '◇',
1862
+ type: 'τ',
1863
+ enum: '∈',
1864
+ const: '●',
1865
+ component: '⬡',
1866
+ hook: '⚓',
1867
+ };
1868
+ for (const symbol of symbols) {
1869
+ const icon = kindIcons[symbol.kind] ?? '○';
1870
+ const exported = symbol.isExported ? chalk.green('↗') : ' ';
1871
+ const async = symbol.isAsync ? chalk.gray('async') : '';
1872
+ console.log(` ${icon}${exported} ${chalk.white(symbol.name)} ${chalk.gray(`(${symbol.kind})`)} ${async}`);
1873
+ console.log(` line ${symbol.lineStart}${symbol.lineEnd !== symbol.lineStart ? `-${symbol.lineEnd}` : ''}`);
1874
+ }
1875
+ }
1876
+ return;
1877
+ }
1878
+ catch {
1879
+ // Skip malformed records
1880
+ }
1881
+ }
1882
+ console.log(chalk.yellow(` File not found in index: ${file}`));
1883
+ console.log(chalk.gray(' Run: cmp-standards wiki index'));
1884
+ });
1885
+ // =============================================================================
1399
1886
  // COMPLETION COMMAND
1400
1887
  // =============================================================================
1401
1888
  program
@@ -1407,7 +1894,7 @@ program
1407
1894
  'init', 'sync', 'validate', 'verify-hooks', 'generate', 'scan', 'improve',
1408
1895
  'status', 'dashboard', 'mcp', 'analytics', 'cross-analytics', 'feedback',
1409
1896
  'tasks', 'ideas', 'improvements', 'plan', 'capture', 'export',
1410
- 'cloud', 'quickstart', 'doctor', 'completion'
1897
+ 'cloud', 'quickstart', 'doctor', 'opportunities', 'completion'
1411
1898
  ];
1412
1899
  const subcommands = {
1413
1900
  cloud: ['status', 'init', 'improvements', 'tasks', 'session']