cmp-standards 3.1.2 → 3.3.1
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.
- package/dist/cli/index.js +488 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +102 -2
- package/dist/db/migrations.js.map +1 -1
- package/dist/eslint/ast-types.d.ts +235 -0
- package/dist/eslint/ast-types.d.ts.map +1 -0
- package/dist/eslint/ast-types.js +9 -0
- package/dist/eslint/ast-types.js.map +1 -0
- package/dist/eslint/rules/consistent-error-handling.d.ts.map +1 -1
- package/dist/eslint/rules/consistent-error-handling.js +2 -1
- package/dist/eslint/rules/consistent-error-handling.js.map +1 -1
- package/dist/eslint/rules/no-async-useeffect.js.map +1 -1
- package/dist/events/EventBus.js.map +1 -1
- package/dist/events/types.d.ts +174 -4
- package/dist/events/types.d.ts.map +1 -1
- package/dist/events/types.js +15 -0
- package/dist/events/types.js.map +1 -1
- package/dist/hooks/session-start.js +3 -3
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +8 -4
- package/dist/mcp/server.js.map +1 -1
- package/dist/patterns/feedback-loop.d.ts +2 -2
- package/dist/patterns/lifecycle.d.ts.map +1 -1
- package/dist/patterns/lifecycle.js +11 -13
- package/dist/patterns/lifecycle.js.map +1 -1
- package/dist/patterns/registry.d.ts +2 -2
- package/dist/plugins/PluginManager.d.ts.map +1 -1
- package/dist/plugins/PluginManager.js +4 -1
- package/dist/plugins/PluginManager.js.map +1 -1
- package/dist/schema/codewiki-types.d.ts +1899 -0
- package/dist/schema/codewiki-types.d.ts.map +1 -0
- package/dist/schema/codewiki-types.js +585 -0
- package/dist/schema/codewiki-types.js.map +1 -0
- package/dist/schema/expert-types.d.ts +2 -2
- package/dist/schema/opportunity-types.d.ts +505 -0
- package/dist/schema/opportunity-types.d.ts.map +1 -0
- package/dist/schema/opportunity-types.js +255 -0
- package/dist/schema/opportunity-types.js.map +1 -0
- package/dist/services/AuditLog.d.ts.map +1 -1
- package/dist/services/AuditLog.js +4 -1
- package/dist/services/AuditLog.js.map +1 -1
- package/dist/services/CodeWikiIndexer.d.ts +145 -0
- package/dist/services/CodeWikiIndexer.d.ts.map +1 -0
- package/dist/services/CodeWikiIndexer.js +664 -0
- package/dist/services/CodeWikiIndexer.js.map +1 -0
- package/dist/services/OpportunityDiscovery.d.ts +84 -0
- package/dist/services/OpportunityDiscovery.d.ts.map +1 -0
- package/dist/services/OpportunityDiscovery.js +754 -0
- package/dist/services/OpportunityDiscovery.js.map +1 -0
- package/dist/services/ProjectScanner.d.ts.map +1 -1
- package/dist/services/ProjectScanner.js +1 -1
- package/dist/services/ProjectScanner.js.map +1 -1
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +2 -0
- package/dist/services/index.js.map +1 -1
- package/dist/utils/env.d.ts +149 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +223 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/logger.d.ts +126 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +231 -0
- package/dist/utils/logger.js.map +1 -0
- 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']
|