(options: FeatureHubOptions)
| 35 | } |
| 36 | |
| 37 | export async function getFeatureHub(options: FeatureHubOptions): Promise<string> { |
| 38 | const { rootDir, showOrphans } = options; |
| 39 | const out: string[] = []; |
| 40 | |
| 41 | if (!options.hubPath && !options.featureName && !showOrphans) { |
| 42 | const hubs = await discoverHubs(rootDir); |
| 43 | if (hubs.length === 0) { |
| 44 | return "No hub files found. Create a .md file with [[path/to/file]] links to establish a feature hub."; |
| 45 | } |
| 46 | out.push(`Feature Hubs (${hubs.length}):`); |
| 47 | out.push(""); |
| 48 | for (const h of hubs) { |
| 49 | const info = await parseHubFile(resolve(rootDir, h)); |
| 50 | out.push(` ${h} | ${info.title} | ${info.links.length} links`); |
| 51 | } |
| 52 | return out.join("\n"); |
| 53 | } |
| 54 | |
| 55 | if (showOrphans) { |
| 56 | const entries = await walkDirectory({ rootDir, depthLimit: 10 }); |
| 57 | const filePaths = entries.filter((e) => !e.isDirectory).map((e) => e.relativePath); |
| 58 | const orphans = await findOrphanedFiles(rootDir, filePaths); |
| 59 | if (orphans.length === 0) return "No orphaned files. All source files are linked to a hub."; |
| 60 | |
| 61 | out.push(`Orphaned Files (${orphans.length}):`); |
| 62 | out.push("These files are not linked to any feature hub:"); |
| 63 | out.push(""); |
| 64 | for (const o of orphans) out.push(` ⚠ ${o}`); |
| 65 | out.push(""); |
| 66 | out.push("Fix: Add [[" + orphans[0] + "]] to the appropriate hub .md file."); |
| 67 | return out.join("\n"); |
| 68 | } |
| 69 | |
| 70 | let hubRelPath = options.hubPath; |
| 71 | if (!hubRelPath && options.featureName) { |
| 72 | hubRelPath = (await findHubByName(rootDir, options.featureName)) ?? undefined; |
| 73 | if (!hubRelPath) { |
| 74 | return `No hub found for feature "${options.featureName}". Available hubs:\n` + |
| 75 | (await discoverHubs(rootDir)).map((h) => ` - ${h}`).join("\n") || " (none)"; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | if (!hubRelPath) return "Provide hub_path, feature_name, or set show_orphans=true."; |
| 80 | |
| 81 | const hubFull = resolve(rootDir, hubRelPath); |
| 82 | if (!(await fileExists(hubFull))) { |
| 83 | return `Hub file not found: ${hubRelPath}`; |
| 84 | } |
| 85 | |
| 86 | const hub = await parseHubFile(hubFull); |
| 87 | |
| 88 | out.push(`Hub: ${hub.title}`); |
| 89 | out.push(`Path: ${hubRelPath}`); |
| 90 | out.push(`Links: ${hub.links.length}`); |
| 91 | if (hub.crossLinks.length > 0) { |
| 92 | out.push(`Cross-links: ${hub.crossLinks.map((c) => c.hubName).join(", ")}`); |
| 93 | } |
| 94 | out.push(""); |
no test coverage detected